Bug Summary

File:Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/rendering/RenderBlockFlow.cpp
Warning:line 4520, column 43
Call argument for 'this' parameter is uncounted and unsafe

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple arm64-apple-ios26.2.0-simulator -O3 -Wundef-prefix=TARGET_OS_ -Werror=undef-prefix -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -Werror=implicit-function-declaration -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name UnifiedSource410.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=osx -analyzer-checker=security.insecureAPI.decodeValueOfObjCType -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=non-leaf -ffp-contract=on -fno-rounding-math -target-sdk-version=26.2 -fdepfile-entry=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/SDKSettings.json -fcompatibility-qualified-id-block-type-checking -fvisibility-inlines-hidden-static-local-var -fno-modulemap-allow-subdirectory-search -fdefine-target-os-macros -enable-tlsdesc -target-cpu apple-m1 -target-feature +v8.4a -target-feature +aes -target-feature +altnzcv -target-feature +ccdp -target-feature +ccpp -target-feature +complxnum -target-feature +crc -target-feature +dotprod -target-feature +flagm -target-feature +fp-armv8 -target-feature +fp16fml -target-feature +fptoint -target-feature +fullfp16 -target-feature +jsconv -target-feature +lse -target-feature +neon -target-feature +pauth -target-feature +perfmon -target-feature +predres -target-feature +ras -target-feature +rcpc -target-feature +rdm -target-feature +sb -target-feature +sha2 -target-feature +sha3 -target-feature +specrestrict -target-feature +ssbs -target-abi darwinpcs -dwarf-ext-refs -fmodule-format=obj -debug-info-kind=standalone -dwarf-version=5 -debugger-tuning=lldb -fdebug-compilation-dir=/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore -target-linker-version 1230.1 -fcoverage-compilation-dir=/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore -resource-dir /Users/buildbot/Library/Developer/Toolchains/swift-webkit.xctoolchain/usr/lib/clang/21 -dependency-file /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/scan-build-output/StaticAnalyzer/WebCore/WebCore/normal/arm64/UnifiedSource410.d -skip-unused-modulemap-deps -MT dependencies -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk -iquote /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/WebCore-generated-files.hmap -iquote /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/WebCore-project-headers.hmap -isystem /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/PAL/ThirdParty/dav1d/include -isystem /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/SDKAdditions/usr/include -iframework /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/SDKAdditions/System/Library/Frameworks -iframework /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/iphonesimulator26.0-additions.sdk/System/Library/PrivateFrameworks -iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/Frameworks -include-pch /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/PrecompiledHeaders/SharedPrecompiledHeaders/11721353671419897710/WebCorePrefix.h.gch -D _LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE -D NDEBUG -D BUILDING_WEBKIT -D GL_SILENCE_DEPRECATION=1 -D GLES_SILENCE_DEPRECATION=1 -D __clang_analyzer__ -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/WebCore-own-target-headers.hmap -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/WebCore-all-target-headers.hmap -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/include -I PAL -I ForwardingHeaders -I /usr/include/libxslt -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/include/libxml2 -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebCore -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/WebKitAdditions -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/WebKitAdditions -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/iphonesimulator26.0-additions.sdk/usr/local/include -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/webrtc -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/webrtc -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/webm -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/webm -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/PAL.build/PAL.build/DerivedSources -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/PAL.build/Release-iphonesimulator/PAL.build/DerivedSources -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/DerivedSources-normal/arm64 -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/DerivedSources/arm64 -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/DerivedSources -F/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator -F/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator -I/usr/local/include -stdlib=libc++ -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/include/c++/v1 -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include -internal-isystem /Users/buildbot/Library/Developer/Toolchains/swift-webkit.xctoolchain/usr/lib/clang/21/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/include -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/Frameworks -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/SubFrameworks -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/Library/Frameworks -Wno-trigraphs -Werror -Wno-missing-field-initializers -Wmissing-prototypes -Wunreachable-code -Wnon-virtual-dtor -Wno-overloaded-virtual -Wno-exit-time-destructors -Wno-missing-braces -Wparentheses -Wswitch -Wunused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wno-float-conversion -Wnon-literal-null-conversion -Wobjc-literal-conversion -Wsign-compare -Wno-shorten-64-to-32 -Wnewline-eof -Wno-c++11-extensions -Wno-implicit-fallthrough -Wdeprecated-declarations -Winvalid-offsetof -Wno-sign-conversion -Winfinite-recursion -Wmove -Wcomma -Wblock-capture-autoreleasing -Wstrict-prototypes -Wrange-loop-analysis -Wno-semicolon-before-method-body -Wall -Wc99-designator -Wconditional-uninitialized -Wextra -Wdeprecated-enum-enum-conversion -Wdeprecated-enum-float-conversion -Wenum-float-conversion -Wfinal-dtor-non-final-class -Wformat=2 -Wmisleading-indentation -Wpointer-to-int-cast -Wreorder-init-list -Wundef -Wunused-but-set-variable -Wvla -Werror=unguarded-availability -Wno-elaborated-enum-base -Wcast-qual -Wchar-subscripts -Wextra-tokens -Winit-self -Wmissing-noreturn -Wpacked -Wpointer-arith -Wredundant-decls -Wwrite-strings -Wexit-time-destructors -Wglobal-constructors -Wtautological-compare -Wimplicit-fallthrough -Wvla -Wno-unknown-warning-option -Wliteral-conversion -Wthread-safety -Wno-profile-instr-out-of-date -Wno-profile-instr-unprofiled -Wno-backend-plugin -Wunsafe-buffer-usage -std=c++2b -fdeprecated-macro -ferror-limit 19 -fmacro-backtrace-limit=0 -fvisibility=hidden -fvisibility-inlines-hidden -stack-protector 1 -fblocks -fencode-extended-block-signature -fno-rtti -fregister-global-dtors-with-atexit -fgnuc-version=4.2.1 -fno-cxx-modules -fno-implicit-modules -fskip-odr-check-in-gmf -fpascal-strings -fmax-type-align=16 -fdiagnostics-show-note-include-stack -vectorize-loops -vectorize-slp -fsafe-buffer-usage-suggestions -analyzer-output=html -analyzer-config report-in-main-source-file=true -analyzer-config nullability:NoDiagnoseCallsToSystemHeaders=true -analyzer-checker optin.osx.cocoa.localizability.NonLocalizedStringChecker -analyzer-checker security.insecureAPI.UncheckedReturn -analyzer-checker security.insecureAPI.getpw -analyzer-checker security.insecureAPI.gets -analyzer-checker security.insecureAPI.mkstemp -analyzer-checker security.insecureAPI.mktemp -analyzer-disable-checker security.insecureAPI.rand -analyzer-disable-checker security.insecureAPI.strcpy -analyzer-checker security.insecureAPI.vfork -analyzer-disable-checker alpha,apiModeling,core,cplusplus,deadcode,debug,fuchsia,nullability,optin,osx,security,unix,webkit -analyzer-checker alpha.webkit.ForwardDeclChecker,alpha.webkit.MemoryUnsafeCastChecker,alpha.webkit.NoDeleteChecker,alpha.webkit.NoUncheckedPtrMemberChecker,alpha.webkit.NoUnretainedMemberChecker,alpha.webkit.RetainPtrCtorAdoptChecker,alpha.webkit.UncheckedCallArgsChecker,alpha.webkit.UncheckedLocalVarsChecker,alpha.webkit.UncountedCallArgsChecker,alpha.webkit.UncountedLocalVarsChecker,alpha.webkit.UnretainedCallArgsChecker,alpha.webkit.UnretainedLambdaCapturesChecker,alpha.webkit.UnretainedLocalVarsChecker,webkit.NoUncountedMemberChecker,webkit.RefCntblBaseVirtualDtor,webkit.UncountedLambdaCapturesChecker -analyzer-config max-nodes=10000000 -analyzer-config verbose-report-filename=true -fdwarf2-cfi-asm -o /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/scan-build-output/StaticAnalyzer/WebCore/WebCore/normal/arm64/UnifiedSource410.plist -x c++ /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebCore/unified-sources/UnifiedSource410.cpp
1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2007 David Smith (catfish.man@gmail.com)
5 * Copyright (C) 2003-2026 Apple Inc. All rights reserved.
6 * Copyright (C) 2014-2016 Google Inc. All rights reserved.
7 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25#include "config.h"
26#include "RenderBlockFlow.h"
27
28#include "AXObjectCache.h"
29#include "BlockStepSizing.h"
30#include "DocumentView.h"
31#include "Editor.h"
32#include "ElementInlines.h"
33#include "FloatingObjects.h"
34#include "FrameSelection.h"
35#include "HTMLInputElement.h"
36#include "HTMLTextAreaElement.h"
37#include "HitTestLocation.h"
38#include "InlineIteratorBoxInlines.h"
39#include "InlineIteratorInlineBox.h"
40#include "InlineIteratorLineBoxInlines.h"
41#include "InlineIteratorLogicalOrderTraversal.h"
42#include "InlineIteratorTextBox.h"
43#include "InlineWalker.h"
44#include "LayoutIntegrationLineLayout.h"
45#include "LayoutRepainter.h"
46#include "LegacyInlineTextBox.h"
47#include "LegacyLineLayout.h"
48#include "LegacyRootInlineBox.h"
49#include "LineClampUpdater.h"
50#include "LineSelection.h"
51#include "LocalFrame.h"
52#include "Logging.h"
53#include "RenderBlockFlowInlines.h"
54#include "RenderBlockInlines.h"
55#include "RenderBoxInlines.h"
56#include "RenderCombineText.h"
57#include "RenderCounter.h"
58#include "RenderDeprecatedFlexibleBox.h"
59#include "RenderElementStyleInlines.h"
60#include "RenderFlexibleBox.h"
61#include "RenderInline.h"
62#include "RenderIterator.h"
63#include "RenderLayer.h"
64#include "RenderLayerScrollableArea.h"
65#include "RenderLayoutState.h"
66#include "RenderLineBreak.h"
67#include "RenderListItem.h"
68#include "RenderMarquee.h"
69#include "RenderMultiColumnFlow.h"
70#include "RenderMultiColumnSet.h"
71#include "RenderObjectInlines.h"
72#include "RenderTableCellInlines.h"
73#include "RenderText.h"
74#include "RenderTreeBuilder.h"
75#include "RenderView.h"
76#include "Settings.h"
77#include "StylePrimitiveNumericTypes+Evaluation.h"
78#include "TextAutoSizing.h"
79#include "TextBoxTrimmer.h"
80#include "TextUtil.h"
81#include "VisiblePosition.h"
82#include <ranges>
83#include <wtf/Scope.h>
84#include <wtf/TZoneMallocInlines.h>
85
86namespace WebCore {
87
88WTF_MAKE_TZONE_ALLOCATED_IMPL(RenderBlockFlow)::bmalloc::api::HeapRef RenderBlockFlow::s_heapRef; const TZoneSpecification
RenderBlockFlow::s_heapSpec = { &RenderBlockFlow::s_heapRef
, TZoneSpecification::encodeSize<RenderBlockFlow>(), TZoneSpecification
::encodeAlignment<RenderBlockFlow>(), TZoneSpecification
::encodeCategory<RenderBlockFlow>(), ::bmalloc::api::compactAllocationMode
<RenderBlockFlow>(), TZoneSpecification::encodeDescriptor
<RenderBlockFlow>(), }; void* RenderBlockFlow::operatorNewSlow
(size_t size) { if constexpr (::bmalloc::api::compactAllocationMode
<RenderBlockFlow>() == CompactAllocationMode::Compact) return
::bmalloc::api::tzoneAllocateCompactSlow(size, s_heapSpec); return
::bmalloc::api::tzoneAllocateNonCompactSlow(size, s_heapSpec
); } using __makeBtzoneMallocedInlineMacroSemicolonifier __attribute__
((unused)) = int
;
89WTF_MAKE_TZONE_ALLOCATED_IMPL(RenderBlockFlowRareData)::bmalloc::api::HeapRef RenderBlockFlowRareData::s_heapRef; const
TZoneSpecification RenderBlockFlowRareData::s_heapSpec = { &
RenderBlockFlowRareData::s_heapRef, TZoneSpecification::encodeSize
<RenderBlockFlowRareData>(), TZoneSpecification::encodeAlignment
<RenderBlockFlowRareData>(), TZoneSpecification::encodeCategory
<RenderBlockFlowRareData>(), ::bmalloc::api::compactAllocationMode
<RenderBlockFlowRareData>(), TZoneSpecification::encodeDescriptor
<RenderBlockFlowRareData>(), }; void* RenderBlockFlowRareData
::operatorNewSlow(size_t size) { if constexpr (::bmalloc::api
::compactAllocationMode<RenderBlockFlowRareData>() == CompactAllocationMode
::Compact) return ::bmalloc::api::tzoneAllocateCompactSlow(size
, s_heapSpec); return ::bmalloc::api::tzoneAllocateNonCompactSlow
(size, s_heapSpec); } using __makeBtzoneMallocedInlineMacroSemicolonifier
__attribute__((unused)) = int
;
90
91bool RenderBlock::s_canPropagateFloatIntoSibling = false;
92
93struct SameSizeAsMarginInfo {
94 uint32_t bitfields : 16;
95 LayoutUnit margins[2];
96};
97
98static_assert(sizeof(MarginValues) == sizeof(LayoutUnit[4]), "MarginValues should stay small");
99static_assert(sizeof(RenderBlockFlow::MarginInfo) == sizeof(SameSizeAsMarginInfo), "MarginInfo should stay small");
100
101RenderBlockFlowRareData::RenderBlockFlowRareData(const RenderBlockFlow& block)
102 : m_margins(positiveMarginBeforeDefault(block), negativeMarginBeforeDefault(block), positiveMarginAfterDefault(block), negativeMarginAfterDefault(block))
103 , m_lineBreakToAvoidWidow(-1)
104 , m_didBreakAtLineToAvoidWidow(false)
105{
106}
107
108RenderBlockFlowRareData::~RenderBlockFlowRareData() = default;
109
110// Our MarginInfo state used when laying out block children.
111RenderBlockFlow::MarginInfo::MarginInfo(const RenderBlockFlow& block, IgnoreScrollbarForAfterMargin ignoreScrollbarForAfterMargin)
112{
113 auto& blockStyle = block.style();
114 ASSERT(block.isRenderView() || block.parent())((void)0);
115
116 m_canCollapseWithChildren = !block.createsNewFormattingContext() && !block.isRenderView();
117
118 m_canCollapseMarginBeforeWithChildren = m_canCollapseWithChildren && !block.borderAndPaddingBefore();
119
120 // If any height other than auto is specified in CSS, then we don't collapse our bottom
121 // margins with our children's margins. To do otherwise would be to risk odd visual
122 // effects when the children overflow out of the parent block and yet still collapse
123 // with it. We also don't collapse if we have any bottom border/padding.
124 auto canCollapseMarginAfterWithChildren = [&]() -> bool {
125 if (!m_canCollapseWithChildren)
126 return false;
127 if (!blockStyle.logicalHeight().isAuto())
128 return false;
129 if (block.borderAndPaddingAfter())
130 return false;
131 // FIXME: Check if all callsites are supposed to take scrollbar into account here.
132 return ignoreScrollbarForAfterMargin == IgnoreScrollbarForAfterMargin::Yes ? true : !block.scrollbarLogicalHeight();
133 };
134 m_canCollapseMarginAfterWithChildren = canCollapseMarginAfterWithChildren();
135
136 m_quirkContainer = block.isRenderTableCell() || block.isBody();
137
138 m_positiveMargin = m_canCollapseMarginBeforeWithChildren ? block.maxPositiveMarginBefore() : 0_lu;
139 m_negativeMargin = m_canCollapseMarginBeforeWithChildren ? block.maxNegativeMarginBefore() : 0_lu;
140}
141
142RenderBlockFlow::MarginInfo::MarginInfo(bool canCollapseWithChildren, bool canCollapseMarginBeforeWithChildren, bool canCollapseMarginAfterWithChildren, bool quirkContainer, bool atBeforeSideOfBlock, bool atAfterSideOfBlock, bool hasMarginBeforeQuirk, bool hasMarginAfterQuirk, bool determinedMarginBeforeQuirk, LayoutUnit positiveMargin, LayoutUnit negativeMargin)
143 : m_canCollapseWithChildren(canCollapseWithChildren)
144 , m_canCollapseMarginBeforeWithChildren(canCollapseMarginBeforeWithChildren)
145 , m_canCollapseMarginAfterWithChildren(canCollapseMarginAfterWithChildren)
146 , m_quirkContainer(quirkContainer)
147 , m_atBeforeSideOfBlock(atBeforeSideOfBlock)
148 , m_atAfterSideOfBlock(atAfterSideOfBlock)
149 , m_hasMarginBeforeQuirk(hasMarginBeforeQuirk)
150 , m_hasMarginAfterQuirk(hasMarginAfterQuirk)
151 , m_determinedMarginBeforeQuirk(determinedMarginBeforeQuirk)
152 , m_positiveMargin(positiveMargin)
153 , m_negativeMargin(negativeMargin)
154{
155}
156
157RenderBlockFlow::RenderBlockFlow(Type type, Element& element, RenderStyle&& style, OptionSet<BlockFlowFlag> flags)
158 : RenderBlock(type, element, WTF::move(style), { }, flags)
159#if ENABLE(TEXT_AUTOSIZING)(defined 1 && 1)
160 , m_widthForTextAutosizing(-1)
161 , m_lineCountForTextAutosizing(NOT_SET)
162#endif
163{
164 ASSERT(isRenderBlockFlow())((void)0);
165 setChildrenInline(true);
166}
167
168RenderBlockFlow::RenderBlockFlow(Type type, Document& document, RenderStyle&& style, OptionSet<BlockFlowFlag> flags)
169 : RenderBlock(type, document, WTF::move(style), { }, flags)
170#if ENABLE(TEXT_AUTOSIZING)(defined 1 && 1)
171 , m_widthForTextAutosizing(-1)
172 , m_lineCountForTextAutosizing(NOT_SET)
173#endif
174{
175 ASSERT(isRenderBlockFlow())((void)0);
176 setChildrenInline(true);
177}
178
179// Do not add any code in below destructor. Add it to willBeDestroyed() instead.
180RenderBlockFlow::~RenderBlockFlow() = default;
181
182void RenderBlockFlow::willBeDestroyed()
183{
184 if (!renderTreeBeingDestroyed()) {
185 if (legacyRootBox()) {
186 // We can't wait for RenderBox::destroy to clear the selection,
187 // because by then we will have nuked the line boxes.
188 if (isSelectionBorder())
189 frame().selection().setNeedsSelectionUpdate();
190
191 // If we are an anonymous block, then our line boxes might have children
192 // that will outlast this block. In the non-anonymous block case those
193 // children will be destroyed by the time we return from this function.
194 if (isAnonymousBlock()) {
195 if (auto* childBox = legacyRootBox()->firstChild())
196 childBox->removeFromParent();
197 }
198 } else if (auto* parent = this->parent(); parent && parent->isSVGRenderer())
199 parent->dirtyLineFromChangedChild();
200 }
201
202 if (svgTextLayout())
203 svgTextLayout()->deleteLegacyRootBox();
204
205 RenderBlock::willBeDestroyed();
206}
207
208RenderMultiColumnFlow* RenderBlockFlow::multiColumnFlowSlowCase() const
209{
210 return rareBlockFlowData()->m_multiColumnFlow.get();
211}
212
213RenderBlockFlow* RenderBlockFlow::previousSiblingWithOverhangingFloats(bool& parentHasFloats) const
214{
215 // Attempt to locate a previous sibling with overhanging floats. We skip any elements that are
216 // out of flow (like floating/positioned elements), and we also skip over any objects that may have shifted
217 // to avoid floats.
218 parentHasFloats = false;
219 for (RenderObject* sibling = previousSibling(); sibling; sibling = sibling->previousSibling()) {
220 if (auto* siblingBlock = dynamicDowncast<RenderBlockFlow>(*sibling)) {
221 if (!siblingBlock->avoidsFloats())
222 return siblingBlock;
223 }
224 if (sibling->isFloating())
225 parentHasFloats = true;
226 }
227 return nullptr;
228}
229
230void RenderBlockFlow::rebuildFloatingObjectSetFromIntrudingFloats()
231{
232 if (layoutContext().isSkippedContentRootForLayout(*this))
233 return;
234
235 auto mayHaveStaleFloatingObjects = [&] {
236 if (style().isSkippedRootOrSkippedContent())
237 return true;
238 if (auto wasSkipped = wasSkippedDuringLastLayoutDueToContentVisibility())
239 return *wasSkipped;
240 return false;
241 };
242 if (mayHaveStaleFloatingObjects())
243 m_floatingObjects = { };
244
245 HashSet<CheckedPtr<RenderBox>> oldIntrudingFloatSet;
246
247 if (m_floatingObjects) {
248 m_floatingObjects->setHorizontalWritingMode(isHorizontalWritingMode());
249 if (!childrenInline()) {
250 for (auto& floatingObject : m_floatingObjects->set()) {
251 if (!floatingObject->renderer())
252 continue;
253 if (!floatingObject->isDescendant())
254 oldIntrudingFloatSet.add(floatingObject->renderer());
255 }
256 }
257 m_floatingObjects->clear();
258 }
259
260 // Inline blocks are covered by the isBlockLevelReplacedOrAtomicInline() check in the avoidFloats method.
261 if (avoidsFloats() || isDocumentElementRenderer() || isRenderView() || isFloatingOrOutOfFlowPositioned() || isRenderTableCell()) {
262 if (!oldIntrudingFloatSet.isEmpty())
263 markAllDescendantsWithFloatsForLayout();
264 return;
265 }
266
267 auto parentBlock = [&]() -> CheckedPtr<RenderBlockFlow> {
268 if (auto* blockFlowParent = dynamicDowncast<RenderBlockFlow>(parent()))
269 return blockFlowParent;
270 if (auto* inlineBoxParent = dynamicDowncast<RenderInline>(parent())) {
271 ASSERT(settings().blocksInInlineLayoutEnabled())((void)0);
272 return dynamicDowncast<RenderBlockFlow>(inlineBoxParent->containingBlock());
273 }
274 // We should not process floats if the parent node is not a RenderBlock. Otherwise, we will add
275 // floats in an invalid context. This will cause a crash arising from a bad cast on the parent.
276 // See <rdar://problem/8049753>, where float property is applied on a text node in a SVG.
277 return { };
278 }();
279 if (!parentBlock)
280 return;
281
282 // First add in floats from the parent. Self-collapsing blocks let their parent track any floats that intrude into
283 // them (as opposed to floats they contain themselves) so check for those here too. If margin collapsing has moved
284 // us up past the top a previous sibling then we need to check for floats from the parent too.
285 bool parentHasFloats = false;
286 RenderBlockFlow* previousBlock = previousSiblingWithOverhangingFloats(parentHasFloats);
287 LayoutUnit logicalTopOffset = logicalTop();
288 bool parentHasIntrudingFloats = !parentHasFloats && (!previousBlock || previousBlock->isSelfCollapsingBlock() || previousBlock->logicalTop() > logicalTopOffset) && parentBlock->lowestFloatLogicalBottom() > logicalTopOffset;
289 if (parentHasFloats || parentHasIntrudingFloats)
290 addIntrudingFloats(parentBlock.get(), parentBlock.get(), parentBlock->logicalLeftOffsetForContent(), logicalTopOffset);
291
292 // Add overhanging floats from the previous RenderBlock, but only if it has a float that intrudes into our space.
293 if (previousBlock) {
294 logicalTopOffset -= previousBlock->logicalTop();
295 if (previousBlock->lowestFloatLogicalBottom() > logicalTopOffset)
296 addIntrudingFloats(previousBlock, parentBlock.get(), 0, logicalTopOffset);
297 }
298
299 if (!childrenInline() && !oldIntrudingFloatSet.isEmpty()) {
300 // If there are previously intruding floats that no longer intrude, then children with floats
301 // should also get layout because they might need their floating object lists cleared.
302 if (m_floatingObjects->set().size() < oldIntrudingFloatSet.size())
303 markAllDescendantsWithFloatsForLayout();
304 else {
305 for (auto& floatingObject : m_floatingObjects->set()) {
306 if (!floatingObject->renderer())
307 continue;
308 oldIntrudingFloatSet.remove(floatingObject->renderer());
309 if (oldIntrudingFloatSet.isEmpty())
310 break;
311 }
312 if (!oldIntrudingFloatSet.isEmpty())
313 markAllDescendantsWithFloatsForLayout();
314 }
315 }
316}
317
318void RenderBlockFlow::adjustIntrinsicLogicalWidthsForColumns(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
319{
320 if (!style().columnCount().isAuto() || !style().columnWidth().isAuto()) {
321 // The min/max intrinsic widths calculated really tell how much space elements need when
322 // laid out inside the columns. In order to eventually end up with the desired column width,
323 // we need to convert them to values pertaining to the multicol container.
324 int columnCount = style().columnCount().tryValue().value_or(1).value;
325 LayoutUnit columnWidth;
326 LayoutUnit colGap = columnGap();
327 LayoutUnit gapExtra = (columnCount - 1) * colGap;
328 if (auto columnWidthLength = style().columnWidth().tryLength()) {
329 columnWidth = Style::evaluate<LayoutUnit>(*columnWidthLength, Style::ZoomNeeded { });
330 minLogicalWidth = std::min(minLogicalWidth, columnWidth);
331 } else
332 minLogicalWidth = minLogicalWidth * columnCount + gapExtra;
333 // FIXME: If column-count is auto here, we should resolve it to calculate the maximum
334 // intrinsic width, instead of pretending that it's 1. The only way to do that is by
335 // performing a layout pass, but this is not an appropriate time or place for layout. The
336 // good news is that if height is unconstrained and there are no explicit breaks, the
337 // resolved column-count really should be 1.
338 maxLogicalWidth = std::max(maxLogicalWidth, columnWidth) * columnCount + gapExtra;
339 }
340}
341
342void RenderBlockFlow::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
343{
344 bool needAdjustIntrinsicLogicalWidthsForColumns = true;
345 if (shouldApplySizeOrInlineSizeContainment()) {
346 if (auto width = explicitIntrinsicInnerLogicalWidth()) {
347 minLogicalWidth = width.value();
348 maxLogicalWidth = width.value();
349 needAdjustIntrinsicLogicalWidthsForColumns = false;
350 }
351 } else if (childrenInline())
352 computeInlinePreferredLogicalWidths(minLogicalWidth, maxLogicalWidth);
353 else
354 computeBlockPreferredLogicalWidths(minLogicalWidth, maxLogicalWidth);
355
356 maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth);
357
358 if (needAdjustIntrinsicLogicalWidthsForColumns)
359 adjustIntrinsicLogicalWidthsForColumns(minLogicalWidth, maxLogicalWidth);
360
361 auto resetMinimumWidthForMarqueeIfApplicable = [&] {
362 if (style().textWrapMode() != TextWrapMode::NoWrap || !layer())
363 return;
364 CheckedPtr scrollableArea = layer()->scrollableArea();
365 if (!scrollableArea || !scrollableArea->marquee() || !scrollableArea->marquee()->isHorizontal())
366 return;
367 // A horizontal marquee has no minimum width.
368 minLogicalWidth = { };
369 };
370 resetMinimumWidthForMarqueeIfApplicable();
371
372 if (auto* cell = dynamicDowncast<RenderTableCell>(*this)) {
373 auto [ tableCellWidth, usedZoom ] = cell->styleOrColLogicalWidth();
374 if (auto fixedTableCellWidth = tableCellWidth.tryFixed(); fixedTableCellWidth && fixedTableCellWidth->isPositive())
375 maxLogicalWidth = std::max(minLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(*fixedTableCellWidth));
376 }
377
378 int scrollbarWidth = intrinsicScrollbarLogicalWidthIncludingGutter();
379 maxLogicalWidth += scrollbarWidth;
380 minLogicalWidth += scrollbarWidth;
381}
382
383bool RenderBlockFlow::recomputeLogicalWidthAndColumnWidth()
384{
385 bool changed = recomputeLogicalWidth();
386
387 LayoutUnit oldColumnWidth = computedColumnWidth();
388 computeColumnCountAndWidth();
389
390 return changed || oldColumnWidth != computedColumnWidth();
391}
392
393LayoutUnit RenderBlockFlow::columnGap() const
394{
395 if (style().columnGap().isNormal())
396 return LayoutUnit(style().fontDescription().computedSize()); // "1em" is recommended as the normal gap setting. Matches <p> margins.
397 return Style::evaluate<LayoutUnit>(style().columnGap(), contentBoxLogicalWidth(), Style::ZoomNeeded { });
398}
399
400void RenderBlockFlow::computeColumnCountAndWidth()
401{
402 // Calculate our column width and column count.
403 // FIXME: Can overflow on fast/block/float/float-not-removed-from-next-sibling4.html, see https://bugs.webkit.org/show_bug.cgi?id=68744
404 unsigned desiredColumnCount = 1;
405 LayoutUnit desiredColumnWidth = contentBoxLogicalWidth();
406
407 // For now, we don't support multi-column layouts when printing, since we have to do a lot of work for proper pagination.
408 if (protect(document())->paginated() || (style().columnCount().isAuto() && style().columnWidth().isAuto()) || !style().hasInlineColumnAxis()) {
409 setComputedColumnCountAndWidth(desiredColumnCount, desiredColumnWidth);
410 return;
411 }
412
413 LayoutUnit availWidth = desiredColumnWidth;
414 LayoutUnit colGap = columnGap();
415 LayoutUnit colWidth = std::max(1_lu, Style::evaluate<LayoutUnit>(style().columnWidth().tryLength().value_or(0_css_px), Style::ZoomNeeded { }));
416 unsigned colCount = std::max<unsigned>(1, style().columnCount().tryValue().value_or(1).value);
417
418 if (style().columnWidth().isAuto() && !style().columnCount().isAuto()) {
419 desiredColumnCount = colCount;
420 desiredColumnWidth = std::max<LayoutUnit>(0, (availWidth - ((desiredColumnCount - 1) * colGap)) / desiredColumnCount);
421 } else if (!style().columnWidth().isAuto() && style().columnCount().isAuto()) {
422 desiredColumnCount = std::max<unsigned>(1, ((availWidth + colGap) / (colWidth + colGap)).toUnsigned());
423 desiredColumnWidth = ((availWidth + colGap) / desiredColumnCount) - colGap;
424 } else {
425 desiredColumnCount = std::max<unsigned>(std::min(colCount, ((availWidth + colGap) / (colWidth + colGap)).toUnsigned()), 1);
426 desiredColumnWidth = ((availWidth + colGap) / desiredColumnCount) - colGap;
427 }
428 setComputedColumnCountAndWidth(desiredColumnCount, desiredColumnWidth);
429}
430
431bool RenderBlockFlow::willCreateColumns(std::optional<unsigned> desiredColumnCount) const
432{
433 // The following types are not supposed to create multicol context.
434 if (isRenderFileUploadControl() || isRenderTextControl() || isRenderListBox())
435 return false;
436 if (isRenderSVGBlock())
437 return false;
438 if (style().display() == Style::DisplayType::BlockRuby || style().display() == Style::DisplayType::RubyText)
439 return false;
440#if ENABLE(MATHML)(defined 1 && 1)
441 if (isRenderMathMLBlock())
442 return false;
443#endif // ENABLE(MATHML)
444
445 if (!firstChild())
446 return false;
447
448 if (style().pseudoElementType())
449 return false;
450
451 // If overflow-y is set to paged-x or paged-y on the body or html element, we'll handle the paginating in the RenderView instead.
452 if ((style().overflowY() == Overflow::PagedX || style().overflowY() == Overflow::PagedY) && !(isDocumentElementRenderer() || isBody()))
453 return true;
454
455 if (!style().specifiesColumns())
456 return false;
457
458 // column-axis with opposite writing direction initiates MultiColumnFlow.
459 if (!style().hasInlineColumnAxis())
460 return true;
461
462 // Non-auto column-width or column-count always initiates MultiColumnFlow.
463 if (!style().columnWidth().isAuto() || !style().columnCount().isAuto())
464 return true;
465
466 if (desiredColumnCount)
467 return desiredColumnCount.value() > 1;
468
469 ASSERT_NOT_REACHED()((void)0);
470 return false;
471}
472
473void RenderBlockFlow::setChildrenInline(bool value)
474{
475 if (childrenInline() && !value) {
476 setLineLayoutPath(UndeterminedPath);
477 m_lineLayout = std::monostate();
478 }
479
480 RenderBlock::setChildrenInline(value);
481}
482
483void RenderBlockFlow::layoutBlockWithNoChildren()
484{
485 ASSERT(!firstChild())((void)0);
486
487 // Empty block containers produce empty formatting lines which may affect trim-start/end.
488 auto textBoxTrimmer = TextBoxTrimmer { *this };
489 auto repainter = LayoutRepainter { *this };
490
491 // FIXME: Instead of taking floats from previous sibling and forwarding them to next unconditionally, we should completely skip these empty block containers.
492 rebuildFloatingObjectSetFromIntrudingFloats();
493
494 auto computeInlineAxisSize =[&] {
495 updateLogicalWidth();
496 };
497 computeInlineAxisSize();
498
499 auto computeBlockAxisSize =[&] {
500 auto& style = this->style();
501
502 if (!is<RenderTableCell>(*this)) {
503 initMaxMarginValues();
504 setHasMarginBeforeQuirk(style.marginBefore().hasQuirk());
505 setHasMarginAfterQuirk(style.marginAfter().hasQuirk());
506 }
507 setLogicalHeight(borderAndPaddingLogicalHeight() + scrollbarLogicalHeight() + (hasLineIfEmpty() ? lineHeight() : 0_lu));
508 updateLogicalHeight();
509 };
510 computeBlockAxisSize();
511
512 auto computeOverflow = [&] {
513 clearOverflow();
514 addVisualEffectOverflow();
515 addVisualOverflowFromTheme();
516 };
517 computeOverflow();
518
519 auto updateLayerProperties = [&] {
520 updateLayerTransform();
521 };
522 if (hasLayer())
523 updateLayerProperties();
524
525 repainter.repaintAfterLayout();
526}
527
528void RenderBlockFlow::layoutBlock(RelayoutChildren relayoutChildren, LayoutUnit pageLogicalHeight)
529{
530 ASSERT(needsLayout())((void)0);
531
532 if (relayoutChildren == RelayoutChildren::No && simplifiedLayout())
533 return;
534
535 auto isPaginated = [&] {
536 // FIXME: Grid calls into layout outside of regular layout phase (during preferred width computation).
537 if (auto* layoutState = view().frameView().layoutContext().layoutState())
538 return layoutState->isPaginated();
539 return false;
540 }();
541
542 if (!firstChild() && !isPaginated && !is<RenderMultiColumnSet>(*this) && (parent() && parent()->isBlockContainer()))
543 return layoutBlockWithNoChildren();
544
545 LayoutRepainter repainter(*this);
546
547 if (recomputeLogicalWidthAndColumnWidth())
548 relayoutChildren = RelayoutChildren::Yes;
549
550 if (auto* layoutState = view().frameView().layoutContext().layoutState(); layoutState && layoutState->legacyLineClamp() && !isFieldset())
551 relayoutChildren = RelayoutChildren::Yes;
552
553 rebuildFloatingObjectSetFromIntrudingFloats();
554
555 LayoutUnit previousHeight = logicalHeight();
556 // FIXME: should this start out as borderAndPaddingLogicalHeight() + scrollbarLogicalHeight(),
557 // for consistency with other render classes?
558 resetLogicalHeightBeforeLayoutIfNeeded();
559
560 bool pageLogicalHeightChanged = false;
561 checkForPaginationLogicalHeightChange(relayoutChildren, pageLogicalHeight, pageLogicalHeightChanged);
562
563 LayoutUnit repaintLogicalTop;
564 LayoutUnit repaintLogicalBottom;
565 LayoutUnit maxFloatLogicalBottom;
566 LayoutUnit pageRemaining;
567 const RenderStyle& styleToUse = style();
568 do {
569 LayoutStateMaintainer statePusher(*this, locationOffset(), isTransformed() || hasReflection() || styleToUse.writingMode().isBlockFlipped(), pageLogicalHeight, pageLogicalHeightChanged);
570
571 preparePaginationBeforeBlockLayout(relayoutChildren);
572 if (isPaginated)
573 pageRemaining = pageLogicalHeightForOffset(0_lu);
574
575 // We use four values, maxTopPos, maxTopNeg, maxBottomPos, and maxBottomNeg, to track
576 // our current maximal positive and negative margins. These values are used when we
577 // are collapsed with adjacent blocks, so for example, if you have block A and B
578 // collapsing together, then you'd take the maximal positive margin from both A and B
579 // and subtract it from the maximal negative margin from both A and B to get the
580 // true collapsed margin. This algorithm is recursive, so when we finish layout()
581 // our block knows its current maximal positive/negative values.
582 //
583 // Start out by setting our margin values to our current margins. Table cells have
584 // no margins, so we don't fill in the values for table cells.
585 bool isCell = isRenderTableCell();
586 if (!isCell) {
587 initMaxMarginValues();
588
589 setHasMarginBeforeQuirk(styleToUse.marginBefore().hasQuirk());
590 setHasMarginAfterQuirk(styleToUse.marginAfter().hasQuirk());
591 setPaginationStrut(0);
592 }
593 if (!firstChild() && !isAnonymousBlock())
594 setChildrenInline(true);
595 dirtyForLayoutFromPercentageHeightDescendants();
596 layoutInFlowChildren(relayoutChildren, previousHeight, repaintLogicalTop, repaintLogicalBottom, maxFloatLogicalBottom);
597 // Expand our intrinsic height to encompass floats.
598 LayoutUnit toAdd = borderAndPaddingAfter() + scrollbarLogicalHeight();
599 if (lowestFloatLogicalBottom() > (logicalHeight() - toAdd) && createsNewFormattingContext())
600 setLogicalHeight(lowestFloatLogicalBottom() + toAdd);
601 if (shouldBreakAtLineToAvoidWidow()) {
602 setEverHadLayout();
603 continue;
604 }
605 break;
606 } while (true);
607
608 if (relayoutForPagination()) {
609 ASSERT(!shouldBreakAtLineToAvoidWidow())((void)0);
610 return;
611 }
612
613 // Calculate our new height.
614 LayoutUnit oldHeight = logicalHeight();
615 auto afterPaddingEdge = clientLogicalBottom();
616
617 // Before updating the final size of the flow thread make sure a forced break is applied after the content.
618 // This ensures the size information is correctly computed for the last auto-height fragment receiving content.
619 if (CheckedPtr fragmentedFlow = dynamicDowncast<RenderFragmentedFlow>(*this))
620 fragmentedFlow->applyBreakAfterContent(afterPaddingEdge);
621
622 updateLogicalHeight();
623 LayoutUnit newHeight = logicalHeight();
624
625 LayoutUnit alignContentShift;
626 auto shouldApplyAlignContent = [&] {
627 // Alignment isn't supported when fragmenting.
628 if (isPaginated && pageRemaining <= newHeight)
629 return false;
630 // Table cell alignment is handled in RenderTableCell::computeIntrinsicPadding.
631 if (isRenderTableCell())
632 return false;
633 return !is<HTMLInputElement>(element());
634 };
635 if (shouldApplyAlignContent()) {
636 alignContentShift = shiftForAlignContent(oldHeight, repaintLogicalTop, repaintLogicalBottom);
637 afterPaddingEdge += alignContentShift;
638 if (alignContentShift < 0)
639 ensureRareBlockFlowData().m_alignContentShift = alignContentShift;
640 } else if (hasRareBlockFlowData())
641 rareBlockFlowData()->m_alignContentShift = { };
642
643 {
644 // FIXME: This could be removed once relayoutForPagination() either stop recursing or we manage to
645 // re-order them.
646 LayoutStateMaintainer statePusher(*this, locationOffset(), isTransformed() || hasReflection() || styleToUse.writingMode().isBlockFlipped(), pageLogicalHeight, pageLogicalHeightChanged);
647
648 if (oldHeight != newHeight) {
649 if (oldHeight > newHeight && maxFloatLogicalBottom > newHeight && !childrenInline()) {
650 // One of our children's floats may have become an overhanging float for us. We need to look for it.
651 for (auto& blockFlow : childrenOfType<RenderBlockFlow>(*this)) {
652 if (blockFlow.isFloatingOrOutOfFlowPositioned())
653 continue;
654 if (blockFlow.lowestFloatLogicalBottom() + blockFlow.logicalTop() > newHeight)
655 addOverhangingFloats(blockFlow, false);
656 }
657 }
658 }
659
660 // Add overflow from children (unless we're multi-column, since in that case all our child overflow is clipped anyway).
661 auto contentArea = flippedContentBoxRect();
662 if (writingMode().isHorizontal())
663 contentArea.shiftMaxYEdgeTo(afterPaddingEdge - paddingAfter());
664 else
665 contentArea.shiftMaxXEdgeTo(afterPaddingEdge - paddingAfter());
666 updateInFlowDescendantTransformsAfterLayout();
667 computeInFlowOverflow(contentArea, isHorizontalWritingMode() ? ComputeOverflowOptions::MarginsExtendContentAreaX : ComputeOverflowOptions::MarginsExtendContentAreaY);
668
669 if (isDocumentElementRenderer() || previousHeight != newHeight || alignContentShift)
670 layoutOutOfFlowBoxes(RelayoutChildren::Yes);
671 else
672 layoutOutOfFlowBoxes(relayoutChildren);
673 updateOutOfFlowDescendantTransformsAfterLayout();
674 addOverflowFromOutOfFlowBoxes();
675 }
676
677 auto* state = view().frameView().layoutContext().layoutState();
678 if (state && state->pageLogicalHeight())
679 setPageLogicalOffset(state->pageLogicalOffset(this, logicalTop()));
680
681 updateLayerTransform();
682
683 // FIXME: This repaint logic should be moved into a separate helper function!
684 // Repaint with our new bounds if they are different from our old bounds.
685 bool didFullRepaint = repainter.repaintAfterLayout();
686 if (!didFullRepaint && repaintLogicalTop != repaintLogicalBottom && (styleToUse.usedVisibility() == Visibility::Visible || enclosingLayer()->hasVisibleContent())) {
687 // FIXME: We could tighten up the left and right invalidation points if we let layoutInlineChildren fill them in based off the particular lines
688 // it had to lay out. We wouldn't need the hasNonVisibleOverflow() hack in that case either.
689 LayoutUnit repaintLogicalLeft = logicalLeftVisualOverflow();
690 LayoutUnit repaintLogicalRight = logicalRightVisualOverflow();
691 if (hasNonVisibleOverflow()) {
692 // If we have clipped overflow, we should use layout overflow as well, since visual overflow from lines didn't propagate to our block's overflow.
693 // Note the old code did this as well but even for overflow:visible. The addition of hasNonVisibleOverflow() at least tightens up the hack a bit.
694 // layoutInlineChildren should be patched to compute the entire repaint rect.
695 repaintLogicalLeft = std::min(repaintLogicalLeft, logicalLeftLayoutOverflow());
696 repaintLogicalRight = std::max(repaintLogicalRight, logicalRightLayoutOverflow());
697 }
698
699 LayoutRect repaintRect;
700 if (isHorizontalWritingMode())
701 repaintRect = LayoutRect(repaintLogicalLeft, repaintLogicalTop, repaintLogicalRight - repaintLogicalLeft, repaintLogicalBottom - repaintLogicalTop);
702 else
703 repaintRect = LayoutRect(repaintLogicalTop, repaintLogicalLeft, repaintLogicalBottom - repaintLogicalTop, repaintLogicalRight - repaintLogicalLeft);
704
705 if (hasNonVisibleOverflow()) {
706 // Adjust repaint rect for scroll offset
707 repaintRect.moveBy(-scrollPosition());
708
709 // Don't allow this rect to spill out of our overflow box.
710 repaintRect.intersect(LayoutRect(LayoutPoint(), size()));
711 }
712
713 // Make sure the rect is still non-empty after intersecting for overflow above
714 if (!repaintRect.isEmpty()) {
715 repaintRectangle(repaintRect); // We need to do a partial repaint of our content.
716 if (hasReflection())
717 repaintRectangle(reflectedRect(repaintRect));
718 }
719 }
720}
721
722void RenderBlockFlow::dirtyForLayoutFromPercentageHeightDescendants()
723{
724 auto* descendants = percentHeightDescendants();
725 if (!descendants)
726 return;
727
728 for (auto& descendant : *descendants) {
729 // Let's not dirty the height perecentage descendant when it has an absolutely positioned containing block ancestor. We should be able to dirty such boxes through the regular invalidation logic.
730 bool descendantNeedsLayout = true;
731 for (auto* ancestor = descendant.containingBlock(); ancestor && ancestor != this; ancestor = ancestor->containingBlock()) {
732 if (ancestor->isOutOfFlowPositioned()) {
733 descendantNeedsLayout = false;
734 break;
735 }
736 }
737 if (!descendantNeedsLayout)
738 continue;
739
740 for (CheckedPtr<RenderElement> renderer = &descendant; renderer && renderer != this && !renderer->normalChildNeedsLayout(); renderer = renderer->container()) {
741 renderer->setChildNeedsLayout(MarkOnlyThis);
742 if (CheckedPtr renderBox = dynamicDowncast<RenderBox>(*renderer)) {
743 // If the width of an image is affected by the height of a child (e.g., an image with an aspect ratio),
744 // then we have to dirty preferred widths, since even enclosing blocks can become dirty as a result.
745 // (A horizontal flexbox that contains an inline image wrapped in an anonymous block for example.)
746 if (renderBox->hasIntrinsicAspectRatio() || renderBox->style().aspectRatio().hasRatio())
747 renderBox->setNeedsPreferredWidthsUpdate();
748 }
749 }
750 }
751}
752
753LayoutUnit RenderBlockFlow::shiftForAlignContent(LayoutUnit intrinsicLogicalHeight, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom)
754{
755 auto alignment = style().alignContent().resolve();
756
757 // Exit if no alignment necessary.
758 if (alignment.isNormal() || alignment.isStartward())
759 return 0_lu;
760
761 // Calculate alignment shift.
762 LayoutUnit computedLogicalHeight = logicalHeight();
763 LayoutUnit space = computedLogicalHeight - intrinsicLogicalHeight;
764 if (space <= 0) {
765 bool overflowIsSafe = (alignment.overflow() == OverflowAlignment::Default && !isScrollContainerY())
766 || alignment.overflow() == OverflowAlignment::Safe
767 || alignment.position() == ContentPosition::Normal;
768 if (overflowIsSafe)
769 return 0_lu; // Floored at zero; we're done
770 }
771 if (alignment.isCentered())
772 space = space / 2;
773
774 // Now shift all our content.
775 if (CheckedPtr inlineLayout = this->inlineLayout())
776 inlineLayout->shiftLinesByInBlockDirection(space);
777 else if (auto* svgTextLayout = this->svgTextLayout()) {
778 if (isHorizontalWritingMode())
779 svgTextLayout->shiftLineBy(0, space);
780 else
781 svgTextLayout->shiftLineBy(-space, 0);
782 } else {
783 for (CheckedPtr child = firstChildBox(); child; child = child->nextSiblingBox()) {
784 setLogicalTopForChild(*child, logicalTopForChild(*child) + space);
785 if (child->isOutOfFlowPositioned() && child->style().hasStaticBlockPosition(isHorizontalWritingMode())) {
786 ASSERT(child->layer())((void)0);
787 child->layer()->setStaticBlockPosition(child->layer()->staticBlockPosition() + space);
788 child->setChildNeedsLayout(MarkOnlyThis);
789 }
790 }
791 }
792 if (m_floatingObjects)
793 m_floatingObjects->shiftFloatsBy(space);
794
795 // Update repaint region.
796 if (space < 0_lu)
797 repaintLogicalTop += space;
798 else
799 repaintLogicalBottom += space;
800
801 return space;
802}
803
804void RenderBlockFlow::layoutInFlowChildren(RelayoutChildren relayoutChildren, LayoutUnit previousHeight, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom, LayoutUnit& maxFloatLogicalBottom)
805{
806 if (!firstChild()) {
807 // Empty block containers produce empty formatting lines which may affect trim-start/end.
808 auto textBoxTrimmer = TextBoxTrimmer { *this };
809
810 auto logicalHeight = borderAndPaddingLogicalHeight() + scrollbarLogicalHeight();
811 if (hasLineIfEmpty())
812 logicalHeight += lineHeight();
813 setLogicalHeight(logicalHeight);
814
815 repaintLogicalTop = { };
816 repaintLogicalBottom = { };
817 maxFloatLogicalBottom = { };
818 return;
819 }
820
821 // FIXME: We should bail out sooner when subtree layout entry point is _inside_ a skipped subtree.
822 if ((layoutContext().isSkippedContentRootForLayout(*this) || layoutContext().isSkippedContentForLayout(*this)) && !(isRenderMultiColumnFlow() || multiColumnFlow())) {
823 clearNeedsLayoutForSkippedContent();
824 return;
825 }
826
827 {
828 auto textBoxTrimmer = TextBoxTrimmer { *this };
829 auto lineClampUpdater = LineClampUpdater { *this };
830 childrenInline() ? layoutInlineChildren(relayoutChildren, previousHeight, repaintLogicalTop, repaintLogicalBottom) : layoutBlockChildren(relayoutChildren, maxFloatLogicalBottom);
831 }
832 {
833 auto applyTextBoxTrimEndIfNeeded = [&] {
834 // With block children and blocks-inside-inline, there's no way to tell what the last formatted line is until after we finished laying out the subtree.
835 // Dirty the last formatted line (in the last IFC) and issue relayout with forcing trimming the last line if applicable.
836 if (CheckedPtr rootForLastFormattedLine = TextBoxTrimmer::lastInlineFormattingContextRootForTrimEnd(*this)) {
837 ASSERT(rootForLastFormattedLine != this)((void)0);
838 // FIXME: We should be able to damage the last line only.
839 for (CheckedPtr<RenderBlock> ancestor = rootForLastFormattedLine; ancestor && ancestor != this; ancestor = ancestor->containingBlock())
840 ancestor->setNeedsLayout(MarkOnlyThis);
841
842 auto textBoxTrimmer = TextBoxTrimmer { *this, *rootForLastFormattedLine };
843 childrenInline() ? layoutInlineChildren(RelayoutChildren::No, previousHeight, repaintLogicalTop, repaintLogicalBottom) : layoutBlockChildren(RelayoutChildren::No, maxFloatLogicalBottom);
844 }
845 };
846 applyTextBoxTrimEndIfNeeded();
847 }
848}
849
850static inline bool isSkippedContentRootOrSkippedContent(const RenderBlockFlow& blockFlow)
851{
852 return isSkippedContentRoot(blockFlow) || blockFlow.isSkippedContent();
853}
854
855void RenderBlockFlow::layoutBlockChildren(RelayoutChildren relayoutChildren, LayoutUnit& maxFloatLogicalBottom)
856{
857 ASSERT(firstChild())((void)0);
858
859 setLogicalHeight(borderAndPaddingBefore());
860 auto* layoutState = view().frameView().layoutContext().layoutState();
861
862 // The margin struct caches all our current margin collapsing state.
863 auto marginInfo = MarginInfo { *this, MarginInfo::IgnoreScrollbarForAfterMargin::No };
864
865 bool marginTrimBlockStartFromContainingBlock = layoutState->marginTrimBlockStart();
866 bool newMarginTrimBlockStartForSubtree = [&] {
867 if (style().marginTrim().contains(Style::MarginTrimSide::BlockStart))
868 return true;
869 if (!marginInfo.canCollapseMarginBeforeWithChildren() && marginTrimBlockStartFromContainingBlock)
870 return false;
871 return marginTrimBlockStartFromContainingBlock;
872 }();
873
874 layoutState->setMarginTrimBlockStart(newMarginTrimBlockStartForSubtree);
875 auto resetBlockStartMarginTrimming = WTF::makeScopeExit([&] {
876 layoutState->setMarginTrimBlockStart(marginTrimBlockStartFromContainingBlock);
877 });
878
879
880 // Fieldsets need to find their legend and position it inside the border of the object.
881 // The legend then gets skipped during normal layout. The same is true for ruby text.
882 // It doesn't get included in the normal layout process but is instead skipped.
883 layoutExcludedChildren(relayoutChildren);
884
885 LayoutUnit previousFloatLogicalBottom;
886 maxFloatLogicalBottom = 0;
887
888 RenderBox* next = firstChildBox();
889
890 while (next) {
891 RenderBox& child = *next;
892 next = child.nextSiblingBox();
893
894 if (child.isExcludedFromNormalLayout())
895 continue; // Skip this child, since it will be positioned by the specialized subclass (fieldsets and ruby runs).
896
897 if (layoutContext().isSkippedContentForLayout(child) && !(isRenderMultiColumnFlow() || multiColumnFlow())) {
898 ASSERT(child.isColumnSpanner())((void)0);
899
900 child.clearNeedsLayout();
901 child.clearNeedsLayoutForSkippedContent();
902 continue;
903 }
904
905 updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, child);
906
907 if (child.isOutOfFlowPositioned()) {
908 child.containingBlock()->addOutOfFlowBox(child);
909 adjustOutOfFlowBlock(child, marginInfo);
910 continue;
911 }
912 if (child.isFloating()) {
913 auto markSiblingsIfIntrudingForLayout = [&] {
914 // Let's find out if this float box is (was) intruding to sibling boxes and mark them for layout accordingly.
915 if (!child.selfNeedsLayout() || !child.everHadLayout()) {
916 // At this point floatingObjectSet() is purged, we can't check whether
917 // this is a new or an existing float in this block container.
918 return;
919 }
920 for (auto* nextSibling = child.nextSibling(); nextSibling; nextSibling = nextSibling->nextSibling()) {
921 CheckedPtr block = dynamicDowncast<RenderBlockFlow>(*nextSibling);
922 if (!block)
923 continue;
924 if (block->avoidsFloats() && !block->shrinkToAvoidFloats())
925 continue;
926 if (isSkippedContentRootOrSkippedContent(*block) || block->containsFloat(child))
927 block->markAllDescendantsWithFloatsForLayout();
928 }
929 };
930 markSiblingsIfIntrudingForLayout();
931 insertFloatingBoxAndMarkForLayout(child);
932 adjustFloatingBlock(marginInfo);
933 continue;
934 }
935
936 // Lay out the child.
937 layoutBlockChild(child, marginInfo, previousFloatLogicalBottom, maxFloatLogicalBottom);
938 }
939
940 if (style().marginTrim().contains(Style::MarginTrimSide::BlockEnd))
941 trimBlockEndChildrenMargins();
942 // Now do the handling of the bottom of the block, adding in our bottom border/padding and
943 // determining the correct collapsed bottom margin information.
944 auto borderBoxLogicalHeight = handleAfterSideOfBlock(marginInfo, logicalHeight() - borderAndPaddingBefore());
945 setLogicalHeight(borderBoxLogicalHeight);
946}
947
948RenderBlockFlow::BlockPositionAndMargin RenderBlockFlow::layoutBlockChildFromInlineLayout(RenderBox& child, LayoutUnit contentHeight, MarginInfo marginInfo)
949{
950 // Render tree uses block height to track the child block layout position. Set it to the current position before calling layoutBlockChild.
951 setLogicalHeight(contentHeight);
952
953 auto previousFloatLogicalBottom = LayoutUnit { };
954 auto maxFloatLogicalBottom = LayoutUnit { };
955 layoutBlockChild(child, marginInfo, previousFloatLogicalBottom, maxFloatLogicalBottom);
956 return { child.logicalTop(), logicalHeight(), marginInfo };
957}
958
959void RenderBlockFlow::trimBlockEndChildrenMargins()
960{
961 auto trimSelfCollapsingChildDescendantsMargins = [&](RenderBox& child) {
962 ASSERT(child.isSelfCollapsingBlock())((void)0);
963 for (auto itr = RenderIterator<RenderBox>(&child, child.firstChildBox()); itr; itr = itr.traverseNext()) {
964 setTrimmedMarginForChild(*itr, Style::MarginTrimSide::BlockStart);
965 setTrimmedMarginForChild(*itr, Style::MarginTrimSide::BlockEnd);
966 }
967 };
968
969 ASSERT(style().marginTrim().contains(Style::MarginTrimSide::BlockEnd))((void)0);
970 // If we are trimming the block end margin, we need to make sure we trim the margin of the children
971 // at the end of the block by walking back up the container. Any self collapsing children will also need to
972 // have their position adjusted to below the last non self-collapsing child in its containing block
973 auto* child = lastChildBox();
974 while (child) {
975 if (child->isExcludedFromNormalLayout() || !child->isInFlow()) {
976 child = child->previousSiblingBox();
977 continue;
978 }
979
980 auto* childContainingBlock = child->containingBlock();
981 setTrimmedMarginForChild(*child, Style::MarginTrimSide::BlockEnd);
982 if (child->isSelfCollapsingBlock()) {
983 setTrimmedMarginForChild(*child, Style::MarginTrimSide::BlockStart);
984 childContainingBlock->setLogicalTopForChild(*child, childContainingBlock->logicalHeight());
985
986 // If this self-collapsing child has any other children, which must also be
987 // self-collapsing, we should trim the margins of all its descendants
988 if (child->firstChildBox() && !child->childrenInline())
989 trimSelfCollapsingChildDescendantsMargins(*child);
990
991 child = child->previousSiblingBox();
992 } else if (auto* nestedBlock = dynamicDowncast<RenderBlockFlow>(child); nestedBlock && nestedBlock->isBlockContainer() && !nestedBlock->childrenInline() && !nestedBlock->style().marginTrim().contains(Style::MarginTrimSide::BlockEnd)) {
993 // The margins *inside* this nested block are protected so we should not introspect and try to trim any of them.
994 if (!MarginInfo { *nestedBlock }.canCollapseMarginAfterWithChildren())
995 break;
996
997 child = child->lastChildBox();
998 } else
999 // We hit another type of block child that doesn't apply to our search. We can just
1000 // end the search since nothing before this block can affect the bottom margin of the outer one we are trimming for.
1001 break;
1002 }
1003}
1004
1005void RenderBlockFlow::simplifiedNormalFlowLayout()
1006{
1007 if (!childrenInline()) {
1008 RenderBlock::simplifiedNormalFlowLayout();
1009 return;
1010 }
1011
1012 bool shouldUpdateOverflow = false;
1013 for (InlineWalker walker(*this); !walker.atEnd(); walker.advance()) {
1014 RenderObject& renderer = *walker.current();
1015 if (auto* box = dynamicDowncast<RenderBox>(renderer)) {
1016 if (!box->isOutOfFlowPositioned() && box->needsLayout()) {
1017 box->layout();
1018 shouldUpdateOverflow = true;
1019 }
1020 continue;
1021 }
1022 if (isAnyOf<RenderText, RenderInline>(renderer))
1023 renderer.clearNeedsLayout();
1024 }
1025
1026 if (!shouldUpdateOverflow)
1027 return;
1028
1029 if (auto* lineLayout = inlineLayout()) {
1030 lineLayout->updateOverflow();
1031 return;
1032 }
1033}
1034
1035void RenderBlockFlow::computeAndSetLineLayoutPath()
1036{
1037 if (lineLayoutPath() != UndeterminedPath)
1038 return;
1039 setLineLayoutPath(LayoutIntegration::LineLayout::canUseFor(*this) ? InlinePath : SvgTextPath);
1040}
1041
1042void RenderBlockFlow::layoutInlineChildren(RelayoutChildren relayoutChildren, LayoutUnit previousHeight, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom)
1043{
1044 computeAndSetLineLayoutPath();
1045
1046 if (lineLayoutPath() == InlinePath)
1047 return layoutInlineContent(relayoutChildren, previousHeight, repaintLogicalTop, repaintLogicalBottom);
1048
1049 if (!svgTextLayout())
1050 m_lineLayout = makeUnique<LegacyLineLayout>(*this);
1051
1052 svgTextLayout()->layoutLineBoxes();
1053 m_previousInlineLayoutContentTopAndBottomIncludingInkOverflow = { };
1054}
1055
1056void RenderBlockFlow::performBlockStepSizing(RenderBox& child, LayoutUnit blockStepSizeForChild) const
1057{
1058 ASSERT(BlockStepSizing::childHasSupportedStyle(child.style()))((void)0);
1059
1060 auto extraSpace = BlockStepSizing::computeExtraSpace(blockStepSizeForChild, logicalMarginBoxHeightForChild(child));
1061 if (!extraSpace)
1062 return;
1063
1064 switch (child.style().blockStepInsert()) {
1065 case BlockStepInsert::MarginBox:
1066 BlockStepSizing::distributeExtraSpaceToChildMargins(child, extraSpace, writingMode());
1067 break;
1068 case BlockStepInsert::ContentBox:
1069 BlockStepSizing::distributeExtraSpaceToChildContentArea(child, extraSpace, writingMode());
1070 break;
1071 case BlockStepInsert::PaddingBox:
1072 BlockStepSizing::distributeExtraSpaceToChildPadding(child, extraSpace, writingMode());
1073 break;
1074 }
1075}
1076
1077void RenderBlockFlow::layoutBlockChild(RenderBox& child, MarginInfo& marginInfo, LayoutUnit& previousFloatLogicalBottom, LayoutUnit& maxFloatLogicalBottom)
1078{
1079 LayoutUnit oldPosMarginBefore = maxPositiveMarginBefore();
1080 LayoutUnit oldNegMarginBefore = maxNegativeMarginBefore();
1081
1082 // The child is a normal flow object. Compute the margins we will use for collapsing now.
1083 child.computeAndSetBlockDirectionMargins(*this);
1084
1085 // Try to guess our correct logical top position. In most cases this guess will
1086 // be correct. Only if we're wrong (when we compute the real logical top position)
1087 // will we have to potentially relayout.
1088 LayoutUnit estimateWithoutPagination;
1089 LayoutUnit logicalTopEstimate = estimateLogicalTopPosition(child, marginInfo, estimateWithoutPagination);
1090
1091 // Cache our old rect so that we can dirty the proper repaint rects if the child moves.
1092 LayoutRect oldRect = child.frameRect();
1093 LayoutUnit oldLogicalTop = logicalTopForChild(child);
1094
1095#if ASSERT_ENABLED0
1096 LayoutSize oldLayoutDelta = view().frameView().layoutContext().layoutDelta();
1097#endif
1098 // Position the child as though it didn't collapse with the top.
1099 setLogicalTopForChild(child, logicalTopEstimate, ApplyLayoutDelta);
1100 estimateFragmentRangeForBoxChild(child);
1101
1102 auto* childBlockFlow = dynamicDowncast<RenderBlockFlow>(child);
1103 bool markDescendantsWithFloats = false;
1104 if (logicalTopEstimate != oldLogicalTop && !child.avoidsFloats() && childBlockFlow && childBlockFlow->containsFloats())
1105 markDescendantsWithFloats = true;
1106 else if (logicalTopEstimate.mightBeSaturated()) [[unlikely]]
1107 // logicalTopEstimate, returned by estimateLogicalTopPosition, might be saturated for
1108 // very large elements. If it does the comparison with oldLogicalTop might yield a
1109 // false negative as adding and removing margins, borders etc from a saturated number
1110 // might yield incorrect results. If this is the case always mark for layout.
1111 markDescendantsWithFloats = true;
1112 else if (!child.avoidsFloats() || child.shrinkToAvoidFloats()) {
1113 // If an element might be affected by the presence of floats, then always mark it for
1114 // layout.
1115 LayoutUnit fb = std::max(previousFloatLogicalBottom, lowestFloatLogicalBottom());
1116 if (fb > logicalTopEstimate)
1117 markDescendantsWithFloats = true;
1118 }
1119
1120 if (childBlockFlow) {
1121 if (markDescendantsWithFloats)
1122 childBlockFlow->markAllDescendantsWithFloatsForLayout();
1123 if (!child.isWritingModeRoot())
1124 previousFloatLogicalBottom = std::max(previousFloatLogicalBottom, oldLogicalTop + childBlockFlow->lowestFloatLogicalBottom());
1125 }
1126
1127 child.markForPaginationRelayoutIfNeeded();
1128
1129 bool childHadLayout = child.everHadLayout();
1130 bool childNeededLayout = child.needsLayout();
1131 if (childNeededLayout)
1132 child.layout();
1133
1134 auto& childStyle = child.style();
1135 if (auto blockStepSizeForChild = childStyle.blockStepSize().tryLength(); blockStepSizeForChild && BlockStepSizing::childHasSupportedStyle(childStyle))
1136 performBlockStepSizing(child, LayoutUnit(blockStepSizeForChild->resolveZoom(Style::ZoomNeeded { })));
1137
1138 // Cache if we are at the top of the block right now.
1139 bool atBeforeSideOfBlock = marginInfo.atBeforeSideOfBlock();
1140
1141 // Now determine the correct ypos based off examination of collapsing margin
1142 // values.
1143 LayoutUnit logicalTopBeforeClear = collapseMargins(child, marginInfo);
1144
1145 // Now check for clear.
1146 LayoutUnit logicalTopAfterClear = clearFloatsIfNeeded(child, marginInfo, oldPosMarginBefore, oldNegMarginBefore, logicalTopBeforeClear);
1147
1148 bool paginated = view().frameView().layoutContext().layoutState()->isPaginated();
1149 if (paginated)
1150 logicalTopAfterClear = adjustBlockChildForPagination(logicalTopAfterClear, estimateWithoutPagination, child, atBeforeSideOfBlock && logicalTopBeforeClear == logicalTopAfterClear);
1151
1152 setLogicalTopForChild(child, logicalTopAfterClear, ApplyLayoutDelta);
1153
1154 // Now we have a final top position. See if it really does end up being different from our estimate.
1155 // clearFloatsIfNeeded can also mark the child as needing a layout even though we didn't move. This happens
1156 // when collapseMargins dynamically adds overhanging floats because of a child with negative margins.
1157 if (logicalTopAfterClear != logicalTopEstimate || child.needsLayout() || (paginated && childBlockFlow && childBlockFlow->shouldBreakAtLineToAvoidWidow())) {
1158 if (child.shrinkToAvoidFloats()) {
1159 // The child's width depends on the line width. When the child shifts to clear an item, its width can
1160 // change (because it has more available line width). So mark the item as dirty.
1161 child.setChildNeedsLayout(MarkOnlyThis);
1162 }
1163
1164 if (childBlockFlow) {
1165 if (!child.avoidsFloats() && childBlockFlow->containsFloats())
1166 childBlockFlow->markAllDescendantsWithFloatsForLayout();
1167 child.markForPaginationRelayoutIfNeeded();
1168 }
1169 }
1170
1171 if (updateFragmentRangeForBoxChild(child))
1172 child.setNeedsLayout(MarkOnlyThis);
1173
1174 // In case our guess was wrong, relayout the child.
1175 child.layoutIfNeeded();
1176
1177 // We are no longer at the top of the block if we encounter a non-empty child.
1178 // This has to be done after checking for clear, so that margins can be reset if a clear occurred.
1179 if (marginInfo.atBeforeSideOfBlock() && !child.isSelfCollapsingBlock()) {
1180 marginInfo.setAtBeforeSideOfBlock(false);
1181
1182 if (auto* layoutState = frame().view()->layoutContext().layoutState(); layoutState && layoutState->marginTrimBlockStart())
1183 layoutState->setMarginTrimBlockStart(false);
1184 }
1185 // Now place the child in the correct left position
1186 determineLogicalLeftPositionForChild(child, ApplyLayoutDelta);
1187
1188 // Update our height now that the child has been placed in the correct position.
1189 setLogicalHeight(logicalHeight() + logicalHeightForChildForFragmentation(child));
1190
1191 // If the child has overhanging floats that intrude into following siblings (or possibly out
1192 // of this block), then the parent gets notified of the floats now.
1193 if (childBlockFlow && childBlockFlow->containsFloats())
1194 maxFloatLogicalBottom = std::max(maxFloatLogicalBottom, addOverhangingFloats(*childBlockFlow, !childNeededLayout));
1195
1196 LayoutSize childOffset = child.location() - oldRect.location();
1197 if (childOffset.width() || childOffset.height()) {
1198 view().frameView().layoutContext().addLayoutDelta(childOffset);
1199
1200 // If the child moved, we have to repaint it as well as any floating/positioned
1201 // descendants. An exception is if we need a layout. In this case, we know we're going to
1202 // repaint ourselves (and the child) anyway.
1203 if (childHadLayout && !selfNeedsLayout() && child.checkForRepaintDuringLayout())
1204 child.repaintDuringLayoutIfMoved(oldRect);
1205 }
1206
1207 if (!childHadLayout && child.checkForRepaintDuringLayout()) {
1208 child.repaint();
1209 child.repaintOverhangingFloats(true);
1210 }
1211
1212 if (paginated) {
1213 if (CheckedPtr fragmentedFlow = enclosingFragmentedFlow())
1214 fragmentedFlow->fragmentedFlowDescendantBoxLaidOut(&child);
1215 // Check for an after page/column break.
1216 LayoutUnit newHeight = applyAfterBreak(child, logicalHeight(), marginInfo);
1217 if (newHeight != height())
1218 setLogicalHeight(newHeight);
1219 }
1220
1221 ASSERT(view().frameView().layoutContext().layoutDeltaMatches(oldLayoutDelta))((void)0);
1222}
1223
1224void RenderBlockFlow::adjustOutOfFlowBlock(RenderBox& child, const MarginInfo& marginInfo)
1225{
1226 bool isHorizontal = isHorizontalWritingMode();
1227 bool hasStaticBlockPosition = child.style().hasStaticBlockPosition(isHorizontal);
1228
1229 LayoutUnit logicalTop = logicalHeight();
1230 updateStaticInlinePositionForChild(child, logicalTop);
1231
1232 if (!marginInfo.canCollapseWithMarginBefore()) {
1233 // Positioned blocks don't collapse margins, so add the margin provided by
1234 // the container now. The child's own margin is added later when calculating its logical top.
1235 LayoutUnit collapsedBeforePos = marginInfo.positiveMargin();
1236 LayoutUnit collapsedBeforeNeg = marginInfo.negativeMargin();
1237 logicalTop += collapsedBeforePos - collapsedBeforeNeg;
1238 }
1239
1240 CheckedPtr childLayer = child.layer();
1241 if (childLayer->staticBlockPosition() != logicalTop) {
1242 childLayer->setStaticBlockPosition(logicalTop);
1243 if (hasStaticBlockPosition)
1244 child.setChildNeedsLayout(MarkOnlyThis);
1245 }
1246}
1247
1248void RenderBlockFlow::determineLogicalLeftPositionForChild(RenderBox& child, ApplyLayoutDeltaMode applyDelta)
1249{
1250 LayoutUnit startPosition = borderAndPaddingStart();
1251 LayoutUnit initialStartPosition = startPosition;
1252 auto verticalScrollbarWidthClampedToContentBox = std::min<LayoutUnit>(verticalScrollbarWidth(), std::max(0_lu, logicalWidth() - borderAndPaddingLogicalWidth()));
1253 if ((shouldPlaceVerticalScrollbarOnLeft() || style().scrollbarGutter().isStableBothEdges()) && isHorizontalWritingMode())
1254 startPosition += (writingMode().isLogicalLeftInlineStart() ? 1 : -1) * verticalScrollbarWidthClampedToContentBox;
1255 if (style().scrollbarGutter().isStableBothEdges() && !isHorizontalWritingMode())
1256 startPosition += (writingMode().isLogicalLeftInlineStart() ? 1 : -1) * horizontalScrollbarHeight();
1257 LayoutUnit totalAvailableLogicalWidth = borderAndPaddingLogicalWidth() + contentBoxLogicalWidth();
1258
1259 LayoutUnit childMarginStart = marginStartForChild(child);
1260 LayoutUnit newPosition = startPosition + childMarginStart;
1261
1262 LayoutUnit positionToAvoidFloats;
1263
1264 if (child.avoidsFloats() && containsFloats())
1265 positionToAvoidFloats = startOffsetForLine(logicalTopForChild(child), logicalHeightForChild(child));
1266
1267 // If the child has an offset from the content edge to avoid floats then use that, otherwise let any negative
1268 // margin pull it back over the content edge or any positive margin push it out.
1269 // If the child is being centred then the margin calculated to do that has factored in any offset required to
1270 // avoid floats, so use it if necessary.
1271
1272 if (style().textAlign() == Style::TextAlign::WebKitCenter || child.style().marginStart(writingMode()).isAuto())
1273 newPosition = std::max(newPosition, positionToAvoidFloats + childMarginStart);
1274 else if (positionToAvoidFloats > initialStartPosition)
1275 newPosition = std::max(newPosition, positionToAvoidFloats);
1276
1277 setLogicalLeftForChild(child, writingMode().isLogicalLeftInlineStart() ? newPosition : totalAvailableLogicalWidth - newPosition - logicalWidthForChild(child), applyDelta);
1278}
1279
1280void RenderBlockFlow::adjustFloatingBlock(const MarginInfo& marginInfo)
1281{
1282 // The float should be positioned taking into account the bottom margin
1283 // of the previous flow. We add that margin into the height, get the
1284 // float positioned properly, and then subtract the margin out of the
1285 // height again. In the case of self-collapsing blocks, we always just
1286 // use the top margins, since the self-collapsing block collapsed its
1287 // own bottom margin into its top margin.
1288 //
1289 // Note also that the previous flow may collapse its margin into the top of
1290 // our block. If this is the case, then we do not add the margin in to our
1291 // height when computing the position of the float. This condition can be tested
1292 // for by simply calling canCollapseWithMarginBefore. See
1293 // http://www.hixie.ch/tests/adhoc/css/box/block/margin-collapse/046.html for
1294 // an example of this scenario.
1295 LayoutUnit marginOffset = marginInfo.canCollapseWithMarginBefore() ? 0_lu : marginInfo.margin();
1296 setLogicalHeight(logicalHeight() + marginOffset);
1297 positionNewFloats();
1298 setLogicalHeight(logicalHeight() - marginOffset);
1299}
1300
1301void RenderBlockFlow::updateStaticInlinePositionForChild(RenderBox& child, LayoutUnit logicalTop)
1302{
1303 if (child.style().originalDisplay().isInlineType())
1304 setStaticInlinePositionForChild(child, staticInlinePositionForOriginalDisplayInline(logicalTop));
1305 else
1306 setStaticInlinePositionForChild(child, startOffsetForContent());
1307}
1308
1309void RenderBlockFlow::setStaticInlinePositionForChild(RenderBox& child, LayoutUnit inlinePosition)
1310{
1311 if (enclosingFragmentedFlow()) {
1312 // Shift the inline position to exclude the fragment offset.
1313 inlinePosition += startOffsetForContent() - startOffsetForContent();
1314 }
1315 child.layer()->setStaticInlinePosition(inlinePosition);
1316}
1317
1318LayoutUnit RenderBlockFlow::staticInlinePositionForOriginalDisplayInline(LayoutUnit logicalTop)
1319{
1320 Style::TextAlign textAlign = style().textAlign();
1321
1322 float logicalLeft = logicalLeftOffsetForLine(logicalTop);
1323 float logicalRight = logicalRightOffsetForLine(logicalTop);
1324
1325 bool isRightAligned = false;
1326 switch (textAlign) {
1327 case Style::TextAlign::Left:
1328 case Style::TextAlign::WebKitLeft:
1329 break;
1330 case Style::TextAlign::Right:
1331 case Style::TextAlign::WebKitRight:
1332 isRightAligned = true;
1333 break;
1334 case Style::TextAlign::Center:
1335 case Style::TextAlign::WebKitCenter:
1336 logicalLeft += (logicalRight - logicalLeft) / 2;
1337 break;
1338 case Style::TextAlign::Justify:
1339 case Style::TextAlign::Start:
1340 if (writingMode().isBidiRTL())
1341 isRightAligned = true;
1342 break;
1343 case Style::TextAlign::End:
1344 if (writingMode().isBidiLTR())
1345 isRightAligned = true;
1346 break;
1347 }
1348
1349 if (isRightAligned == writingMode().isLogicalLeftLineLeft())
1350 logicalLeft = logicalRight;
1351
1352 if (!writingMode().isLogicalLeftInlineStart())
1353 return LayoutUnit(logicalWidth() - logicalLeft);
1354
1355 return LayoutUnit(logicalLeft);
1356}
1357
1358MarginValues RenderBlockFlow::marginValuesForChild(RenderBox& child) const
1359{
1360 LayoutUnit childBeforePositive;
1361 LayoutUnit childBeforeNegative;
1362 LayoutUnit childAfterPositive;
1363 LayoutUnit childAfterNegative;
1364
1365 LayoutUnit beforeMargin;
1366 LayoutUnit afterMargin;
1367
1368 auto* childRenderBlock = dynamicDowncast<RenderBlockFlow>(child);
1369
1370 // If the child has the same directionality as we do, then we can just return its
1371 // margins in the same direction.
1372 if (!child.isWritingModeRoot()) {
1373 if (childRenderBlock) {
1374 childBeforePositive = childRenderBlock->maxPositiveMarginBefore();
1375 childBeforeNegative = childRenderBlock->maxNegativeMarginBefore();
1376 childAfterPositive = childRenderBlock->maxPositiveMarginAfter();
1377 childAfterNegative = childRenderBlock->maxNegativeMarginAfter();
1378 } else {
1379 beforeMargin = child.marginBefore();
1380 afterMargin = child.marginAfter();
1381 }
1382 } else if (child.isHorizontalWritingMode() == isHorizontalWritingMode()) {
1383 // The child has a different directionality. If the child is parallel, then it's just
1384 // flipped relative to us. We can use the margins for the opposite edges.
1385 if (childRenderBlock) {
1386 childBeforePositive = childRenderBlock->maxPositiveMarginAfter();
1387 childBeforeNegative = childRenderBlock->maxNegativeMarginAfter();
1388 childAfterPositive = childRenderBlock->maxPositiveMarginBefore();
1389 childAfterNegative = childRenderBlock->maxNegativeMarginBefore();
1390 } else {
1391 beforeMargin = child.marginAfter();
1392 afterMargin = child.marginBefore();
1393 }
1394 } else {
1395 // The child is perpendicular to us, which means its margins don't collapse but are on the
1396 // "logical left/right" sides of the child box. We can just return the raw margin in this case.
1397 beforeMargin = marginBeforeForChild(child);
1398 afterMargin = marginAfterForChild(child);
1399 }
1400
1401 // Resolve uncollapsing margins into their positive/negative buckets.
1402 if (beforeMargin) {
1403 if (beforeMargin > 0)
1404 childBeforePositive = beforeMargin;
1405 else
1406 childBeforeNegative = -beforeMargin;
1407 }
1408 if (afterMargin) {
1409 if (afterMargin > 0)
1410 childAfterPositive = afterMargin;
1411 else
1412 childAfterNegative = -afterMargin;
1413 }
1414
1415 return MarginValues(childBeforePositive, childBeforeNegative, childAfterPositive, childAfterNegative);
1416}
1417
1418bool RenderBlockFlow::childrenPreventSelfCollapsing() const
1419{
1420 if (!childrenInline())
1421 return RenderBlock::childrenPreventSelfCollapsing();
1422
1423 if (inlineLayout())
1424 return !inlineLayout()->isSelfCollapsingContent();
1425
1426 if (svgTextLayout())
1427 return svgTextLayout()->lineCount();
1428
1429 // Containers with no children.
1430 return false;
1431}
1432
1433LayoutUnit RenderBlockFlow::collapseMargins(RenderBox& child, MarginInfo& marginInfo)
1434{
1435 auto beforeCollapseLogicalTop = logicalHeight();
1436 auto logicalTop = collapseMarginsWithChildInfo(&child, marginInfo);
1437 auto addIntrudingFloatsFromPreviousBlocks = [&] {
1438 for (auto* previousSibling = child.previousSibling(); previousSibling; previousSibling = previousSibling->previousSibling()) {
1439 CheckedPtr previousBlockSibling = dynamicDowncast<RenderBlockFlow>(previousSibling);
1440 if (!previousBlockSibling || previousBlockSibling->createsNewFormattingContext())
1441 continue;
1442 if (previousBlockSibling->logicalTop() + previousBlockSibling->lowestFloatLogicalBottom() <= logicalTop)
1443 break;
1444 // If |child| is a self-collapsing block it may have collapsed into a previous sibling and although it hasn't reduced the height of the parent yet
1445 // any floats from the parent will now overhang.
1446 auto oldLogicalHeight = logicalHeight();
1447 setLogicalHeight(logicalTop);
1448 if (previousBlockSibling->containsFloats() && !previousBlockSibling->avoidsFloats())
1449 addOverhangingFloats(*previousBlockSibling, false);
1450 setLogicalHeight(oldLogicalHeight);
1451 }
1452 };
1453 addIntrudingFloatsFromPreviousBlocks();
1454 // If |child|'s previous sibling is or contains a self-collapsing block that cleared a float and margin collapsing resulted in |child| moving up
1455 // into the margin area of the self-collapsing block then the float it clears is now intruding into |child|. Layout again so that we can look for
1456 // floats in the parent that overhang |child|'s new logical top.
1457 auto logicalTopIntrudesIntoFloat = logicalTop < beforeCollapseLogicalTop;
1458 if (logicalTopIntrudesIntoFloat && containsFloats() && !child.avoidsFloats() && lowestFloatLogicalBottom() > logicalTop)
1459 child.setNeedsLayout(MarkOnlyThis);
1460 return logicalTop;
1461}
1462
1463std::optional<LayoutUnit> RenderBlockFlow::selfCollapsingMarginBeforeWithClear(RenderObject* candidate)
1464{
1465 CheckedPtr candidateBlockFlow = dynamicDowncast<RenderBlockFlow>(candidate);
1466 if (!candidateBlockFlow)
1467 return { };
1468
1469 if (!candidateBlockFlow->isSelfCollapsingBlock())
1470 return { };
1471
1472 if (RenderStyle::usedClear(*candidateBlockFlow) == UsedClear::None || !containsFloats())
1473 return { };
1474
1475 auto clear = computedClearDeltaForChild(*candidateBlockFlow, candidateBlockFlow->logicalHeight());
1476 // Just because a block box has the clear property set, it does not mean we always get clearance (e.g. when the box is below the cleared floats)
1477 if (clear < candidateBlockFlow->logicalBottom())
1478 return { };
1479
1480 return marginValuesForChild(*candidateBlockFlow).positiveMarginBefore();
1481}
1482
1483LayoutUnit RenderBlockFlow::collapseMarginsWithChildInfo(RenderBox* child, MarginInfo& marginInfo)
1484{
1485 bool childIsSelfCollapsing = child ? child->isSelfCollapsingBlock() : false;
1486 bool beforeQuirk = child ? hasMarginBeforeQuirk(*child) : false;
1487 bool afterQuirk = child ? hasMarginAfterQuirk(*child) : false;
1488 auto trimChildBlockMargins = [&]() {
1489 auto childBlockFlow = dynamicDowncast<RenderBlockFlow>(child);
1490 if (childBlockFlow)
1491 childBlockFlow->setMaxMarginBeforeValues(0_lu, 0_lu);
1492 setTrimmedMarginForChild(*child, Style::MarginTrimSide::BlockStart);
1493
1494 // The margin after for a self collapsing child should also be trimmed so it does not
1495 // influence the margins of the first non collapsing child
1496 if (childIsSelfCollapsing) {
1497 if (childBlockFlow)
1498 childBlockFlow->setMaxMarginAfterValues(0_lu, 0_lu);
1499 setTrimmedMarginForChild(*child, Style::MarginTrimSide::BlockEnd);
1500 }
1501 };
1502 if (frame().view()->layoutContext().layoutState()->marginTrimBlockStart()) {
1503 ASSERT(marginInfo.atBeforeSideOfBlock())((void)0);
1504 trimChildBlockMargins();
1505 }
1506
1507 // Get the four margin values for the child and cache them.
1508 MarginValues childMargins = child ? marginValuesForChild(*child) : MarginValues(0, 0, 0, 0);
1509 // Get our max pos and neg top margins.
1510 LayoutUnit posTop = childMargins.positiveMarginBefore();
1511 LayoutUnit negTop = childMargins.negativeMarginBefore();
1512
1513 // For self-collapsing blocks, collapse our bottom margins into our
1514 // top to get new posTop and negTop values.
1515 if (childIsSelfCollapsing) {
1516 posTop = std::max(posTop, childMargins.positiveMarginAfter());
1517 negTop = std::max(negTop, childMargins.negativeMarginAfter());
1518 }
1519
1520 if (marginInfo.canCollapseWithMarginBefore()) {
1521 // This child is collapsing with the top of the
1522 // block. If it has larger margin values, then we need to update
1523 // our own maximal values.
1524 if (!document().inQuirksMode() || !marginInfo.quirkContainer() || !beforeQuirk)
1525 setMaxMarginBeforeValues(std::max(posTop, maxPositiveMarginBefore()), std::max(negTop, maxNegativeMarginBefore()));
1526
1527 // The minute any of the margins involved isn't a quirk, don't
1528 // collapse it away, even if the margin is smaller (www.webreference.com
1529 // has an example of this, a <dt> with 0.8em author-specified inside
1530 // a <dl> inside a <td>.
1531 if (!marginInfo.determinedMarginBeforeQuirk() && !beforeQuirk && (posTop - negTop)) {
1532 setHasMarginBeforeQuirk(false);
1533 marginInfo.setDeterminedMarginBeforeQuirk(true);
1534 }
1535
1536 if (!marginInfo.determinedMarginBeforeQuirk() && beforeQuirk && !marginBefore()) {
1537 // We have no top margin and our top child has a quirky margin.
1538 // We will pick up this quirky margin and pass it through.
1539 // This deals with the <td><div><p> case.
1540 // Don't do this for a block that split two inlines though. You do
1541 // still apply margins in this case.
1542 setHasMarginBeforeQuirk(true);
1543 }
1544 }
1545
1546 if (marginInfo.quirkContainer() && marginInfo.atBeforeSideOfBlock() && (posTop - negTop))
1547 marginInfo.setHasMarginBeforeQuirk(beforeQuirk);
1548
1549 LayoutUnit beforeCollapseLogicalTop = logicalHeight();
1550 LayoutUnit logicalTop = beforeCollapseLogicalTop;
1551 // If the child's previous sibling is a self-collapsing block that cleared a float then its top border edge has been set at the bottom border edge
1552 // of the float. Since we want to collapse the child's top margin with the self-collapsing block's top and bottom margins we need to adjust our parent's height to match the
1553 // margin top of the self-collapsing block. If the resulting collapsed margin leaves the child still intruding into the float then we will want to clear it.
1554 if (!marginInfo.canCollapseWithMarginBefore()) {
1555 if (auto value = selfCollapsingMarginBeforeWithClear(child->previousSibling()))
1556 setLogicalHeight(logicalHeight() - *value);
1557 }
1558
1559 if (childIsSelfCollapsing) {
1560 // This child has no height. We need to compute our
1561 // position before we collapse the child's margins together,
1562 // so that we can get an accurate position for the zero-height block.
1563 LayoutUnit collapsedBeforePos = std::max(marginInfo.positiveMargin(), childMargins.positiveMarginBefore());
1564 LayoutUnit collapsedBeforeNeg = std::max(marginInfo.negativeMargin(), childMargins.negativeMarginBefore());
1565 marginInfo.setMargin(collapsedBeforePos, collapsedBeforeNeg);
1566
1567 // Now collapse the child's margins together, which means examining our
1568 // bottom margin values as well.
1569 marginInfo.setPositiveMarginIfLarger(childMargins.positiveMarginAfter());
1570 marginInfo.setNegativeMarginIfLarger(childMargins.negativeMarginAfter());
1571
1572 if (!marginInfo.canCollapseWithMarginBefore()) {
1573 // We need to make sure that the position of the self-collapsing block
1574 // is correct, since it could have overflowing content
1575 // that needs to be positioned correctly (e.g., a block that
1576 // had a specified height of 0 but that actually had subcontent).
1577 logicalTop = logicalHeight() + collapsedBeforePos - collapsedBeforeNeg;
1578 }
1579 } else {
1580 if (!marginInfo.atBeforeSideOfBlock() || (!marginInfo.canCollapseMarginBeforeWithChildren()
1581 && (!document().inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.hasMarginBeforeQuirk()))) {
1582 // We're collapsing with a previous sibling's margins and not
1583 // with the top of the block.
1584 setLogicalHeight(logicalHeight() + std::max(marginInfo.positiveMargin(), posTop) - std::max(marginInfo.negativeMargin(), negTop));
1585 logicalTop = logicalHeight();
1586 }
1587
1588 marginInfo.setPositiveMargin(childMargins.positiveMarginAfter());
1589 marginInfo.setNegativeMargin(childMargins.negativeMarginAfter());
1590
1591 if (marginInfo.margin())
1592 marginInfo.setHasMarginAfterQuirk(afterQuirk);
1593 }
1594
1595 // If margins would pull us past the top of the next page, then we need to pull back and pretend like the margins
1596 // collapsed into the page edge.
1597 auto* layoutState = view().frameView().layoutContext().layoutState();
1598 if (layoutState->isPaginated() && layoutState->pageLogicalHeight() && logicalTop > beforeCollapseLogicalTop
1599 && hasNextPage(beforeCollapseLogicalTop)) {
1600 LayoutUnit oldLogicalTop = logicalTop;
1601 logicalTop = std::min(logicalTop, nextPageLogicalTop(beforeCollapseLogicalTop));
1602 setLogicalHeight(logicalHeight() + (logicalTop - oldLogicalTop));
1603 }
1604
1605 return logicalTop;
1606}
1607
1608bool RenderBlockFlow::isChildEligibleForMarginTrim(Style::MarginTrimSide marginTrimSide, const RenderBox& child) const
1609{
1610 ASSERT(style().marginTrim().contains(marginTrimSide))((void)0);
1611 if (!child.style().display().isBlockType())
1612 return false;
1613 // https://drafts.csswg.org/css-box-4/#margin-trim-block
1614 // 3.3.1. Trimming Block Container Content
1615 // For block containers specifically, margin-trim discards:
1616 switch (marginTrimSide) {
1617 case Style::MarginTrimSide::BlockStart:
1618 // The block-start margin of a block-level first child, when trimming at the block-start edge.
1619 return firstInFlowChildBox() == &child;
1620 case Style::MarginTrimSide::BlockEnd:
1621 // The block-end margin of a block-level last child, when trimming at the block-end edge.
1622 return lastInFlowChildBox() == &child;
1623 case Style::MarginTrimSide::InlineStart:
1624 case Style::MarginTrimSide::InlineEnd:
1625 // It has no effect on the inline-axis margins of block-level descendants, nor on any margins of inline-level descendants.
1626 return false;
1627 default:
1628 ASSERT_NOT_REACHED()((void)0);
1629 return false;
1630 }
1631}
1632
1633LayoutUnit RenderBlockFlow::clearFloatsIfNeeded(RenderBox& child, MarginInfo& marginInfo, LayoutUnit oldTopPosMargin, LayoutUnit oldTopNegMargin, LayoutUnit yPos)
1634{
1635 LayoutUnit heightIncrease = computedClearDeltaForChild(child, yPos);
1636 if (!heightIncrease)
1637 return yPos;
1638
1639 if (child.isSelfCollapsingBlock()) {
1640 // For self-collapsing blocks that clear, they can still collapse their
1641 // margins with following siblings. Reset the current margins to represent
1642 // the self-collapsing block's margins only.
1643 MarginValues childMargins = marginValuesForChild(child);
1644 marginInfo.setPositiveMargin(std::max(childMargins.positiveMarginBefore(), childMargins.positiveMarginAfter()));
1645 marginInfo.setNegativeMargin(std::max(childMargins.negativeMarginBefore(), childMargins.negativeMarginAfter()));
1646
1647 // CSS2.1 states:
1648 // "If the top and bottom margins of an element with clearance are adjoining, its margins collapse with
1649 // the adjoining margins of following siblings but that resulting margin does not collapse with the bottom margin of the parent block."
1650 // So the parent's bottom margin cannot collapse through this block or any subsequent self-collapsing blocks. Check subsequent siblings
1651 // for a block with height - if none is found then don't allow the margins to collapse with the parent.
1652 bool wouldCollapseMarginsWithParent = marginInfo.canCollapseMarginAfterWithChildren();
1653 for (RenderBox* curr = child.nextSiblingBox(); curr && wouldCollapseMarginsWithParent; curr = curr->nextSiblingBox()) {
1654 if (!curr->isFloatingOrOutOfFlowPositioned() && !curr->isSelfCollapsingBlock())
1655 wouldCollapseMarginsWithParent = false;
1656 }
1657 if (wouldCollapseMarginsWithParent)
1658 marginInfo.setCanCollapseMarginAfterWithChildren(false);
1659
1660 // For now set the border-top of |child| flush with the bottom border-edge of the float so it can layout any floating or positioned children of
1661 // its own at the correct vertical position. If subsequent siblings attempt to collapse with |child|'s margins in |collapseMargins| we will
1662 // adjust the height of the parent to |child|'s margin top (which if it is positive sits up 'inside' the float it's clearing) so that all three
1663 // margins can collapse at the correct vertical position.
1664 // Per CSS2.1 we need to ensure that any negative margin-top clears |child| beyond the bottom border-edge of the float so that the top border edge of the child
1665 // (i.e. its clearance) is at a position that satisfies the equation: "the amount of clearance is set so that clearance + margin-top = [height of float],
1666 // i.e., clearance = [height of float] - margin-top".
1667 setLogicalHeight(child.logicalTop() + childMargins.negativeMarginBefore());
1668 } else
1669 // Increase our height by the amount we had to clear.
1670 setLogicalHeight(logicalHeight() + heightIncrease);
1671
1672 if (marginInfo.canCollapseWithMarginBefore()) {
1673 // We can no longer collapse with the top of the block since a clear
1674 // occurred. The empty blocks collapse into the cleared block.
1675 // https://www.w3.org/TR/CSS2/visuren.html#clearance
1676 // "CSS2.1 - Computing the clearance of an element on which 'clear' is set is done..."
1677 setMaxMarginBeforeValues(oldTopPosMargin, oldTopNegMargin);
1678 marginInfo.setAtBeforeSideOfBlock(false);
1679 }
1680
1681 return yPos + heightIncrease;
1682}
1683
1684void RenderBlockFlow::marginBeforeEstimateForChild(RenderBox& child, LayoutUnit& positiveMarginBefore, LayoutUnit& negativeMarginBefore) const
1685{
1686 // Give up if in quirks mode and we're a body/table cell and the top margin of the child box is quirky.
1687 // Give up if the child specified -webkit-margin-collapse: separate that prevents collapsing.
1688 if (document().inQuirksMode() && hasMarginBeforeQuirk(child) && (isRenderTableCell() || isBody()))
1689 return;
1690
1691 LayoutUnit beforeChildMargin = marginBeforeForChild(child);
1692 positiveMarginBefore = std::max(positiveMarginBefore, beforeChildMargin);
1693 negativeMarginBefore = std::max(negativeMarginBefore, -beforeChildMargin);
1694
1695 CheckedPtr childBlock = dynamicDowncast<RenderBlockFlow>(child);
1696 if (!childBlock)
1697 return;
1698
1699 if (childBlock->childrenInline() || childBlock->isWritingModeRoot())
1700 return;
1701
1702 if (!MarginInfo { *childBlock }.canCollapseMarginBeforeWithChildren())
1703 return;
1704
1705 RenderBox* grandchildBox = childBlock->firstChildBox();
1706 for (; grandchildBox; grandchildBox = grandchildBox->nextSiblingBox()) {
1707 if (!grandchildBox->isFloatingOrOutOfFlowPositioned())
1708 break;
1709 }
1710
1711 if (!grandchildBox)
1712 return;
1713
1714 // Make sure to update the block margins now for the grandchild box so that we're looking at current values.
1715 if (grandchildBox->needsLayout()) {
1716 grandchildBox->computeAndSetBlockDirectionMargins(*this);
1717 if (CheckedPtr grandchildBlock = dynamicDowncast<RenderBlock>(*grandchildBox)) {
1718 grandchildBlock->setHasMarginBeforeQuirk(grandchildBox->style().marginBefore().hasQuirk());
1719 grandchildBlock->setHasMarginAfterQuirk(grandchildBox->style().marginAfter().hasQuirk());
1720 }
1721 }
1722
1723 // If we have a 'clear' value but also have a margin we may not actually require clearance to move past any floats.
1724 // If that's the case we want to be sure we estimate the correct position including margins after any floats rather
1725 // than use 'clearance' later which could give us the wrong position.
1726 if (RenderStyle::usedClear(*grandchildBox) != UsedClear::None && !childBlock->marginBeforeForChild(*grandchildBox))
1727 return;
1728
1729 // Collapse the margin of the grandchild box with our own to produce an estimate.
1730 childBlock->marginBeforeEstimateForChild(*grandchildBox, positiveMarginBefore, negativeMarginBefore);
1731}
1732
1733LayoutUnit RenderBlockFlow::estimateLogicalTopPosition(RenderBox& child, const MarginInfo& marginInfo, LayoutUnit& estimateWithoutPagination)
1734{
1735 // FIXME: We need to eliminate the estimation of vertical position, because when it's wrong we sometimes trigger a pathological
1736 // relayout if there are intruding floats.
1737 LayoutUnit logicalTopEstimate = logicalHeight();
1738 if (!marginInfo.canCollapseWithMarginBefore()) {
1739 LayoutUnit positiveMarginBefore;
1740 LayoutUnit negativeMarginBefore;
1741 if (child.selfNeedsLayout()) {
1742 // Try to do a basic estimation of how the collapse is going to go.
1743 marginBeforeEstimateForChild(child, positiveMarginBefore, negativeMarginBefore);
1744 } else {
1745 // Use the cached collapsed margin values from a previous layout. Most of the time they
1746 // will be right.
1747 MarginValues marginValues = marginValuesForChild(child);
1748 positiveMarginBefore = std::max(positiveMarginBefore, marginValues.positiveMarginBefore());
1749 negativeMarginBefore = std::max(negativeMarginBefore, marginValues.negativeMarginBefore());
1750 }
1751
1752 // Collapse the result with our current margins.
1753 logicalTopEstimate += std::max(marginInfo.positiveMargin(), positiveMarginBefore) - std::max(marginInfo.negativeMargin(), negativeMarginBefore);
1754 }
1755
1756 // Adjust logicalTopEstimate down to the next page if the margins are so large that we don't fit on the current
1757 // page.
1758 auto* layoutState = view().frameView().layoutContext().layoutState();
1759 if (layoutState->isPaginated() && layoutState->pageLogicalHeight() && logicalTopEstimate > logicalHeight()
1760 && hasNextPage(logicalHeight()))
1761 logicalTopEstimate = std::min(logicalTopEstimate, nextPageLogicalTop(logicalHeight()));
1762
1763 logicalTopEstimate += computedClearDeltaForChild(child, logicalTopEstimate);
1764
1765 estimateWithoutPagination = logicalTopEstimate;
1766
1767 if (layoutState->isPaginated()) {
1768 // If the object has a page or column break value of "before", then we should shift to the top of the next page.
1769 logicalTopEstimate = applyBeforeBreak(child, logicalTopEstimate);
1770
1771 // For replaced elements and scrolled elements, we want to shift them to the next page if they don't fit on the current one.
1772 logicalTopEstimate = adjustForUnsplittableChild(child, logicalTopEstimate);
1773
1774 if (!child.selfNeedsLayout()) {
1775 if (auto* block = dynamicDowncast<RenderBlock>(child))
1776 logicalTopEstimate += block->paginationStrut();
1777 }
1778 }
1779
1780 return logicalTopEstimate;
1781}
1782
1783void RenderBlockFlow::setCollapsedBottomMargin(const MarginInfo& marginInfo)
1784{
1785 if (marginInfo.canCollapseWithMarginAfter() && !marginInfo.canCollapseWithMarginBefore()) {
1786 // Update our max pos/neg bottom margins, since we collapsed our bottom margins
1787 // with our children.
1788 auto shouldTrimBlockEndMargin = style().marginTrim().contains(Style::MarginTrimSide::BlockEnd);
1789 auto propagatedPositiveMargin = shouldTrimBlockEndMargin ? 0_lu : marginInfo.positiveMargin();
1790 auto propagatedNegativeMargin = shouldTrimBlockEndMargin ? 0_lu : marginInfo.negativeMargin();
1791 setMaxMarginAfterValues(std::max(maxPositiveMarginAfter(), propagatedPositiveMargin), std::max(maxNegativeMarginAfter(), propagatedNegativeMargin));
1792
1793 if (!marginInfo.hasMarginAfterQuirk())
1794 setHasMarginAfterQuirk(false);
1795
1796 if (marginInfo.hasMarginAfterQuirk() && !marginAfter())
1797 // We have no bottom margin and our last child has a quirky margin.
1798 // We will pick up this quirky margin and pass it through.
1799 // This deals with the <td><div><p> case.
1800 setHasMarginAfterQuirk(true);
1801 }
1802}
1803
1804LayoutUnit RenderBlockFlow::handleAfterSideOfBlock(MarginInfo& marginInfo, LayoutUnit contentBoxLogicalHeight)
1805{
1806 marginInfo.setAtAfterSideOfBlock(true);
1807
1808 // If our last child was a self-collapsing block with clearance then our logical height is flush with the
1809 // bottom edge of the float that the child clears. The correct vertical position for the margin-collapsing we want
1810 // to perform now is at the child's margin-top - so adjust our height to that position.
1811 auto borderBoxLogicalHeight = borderAndPaddingBefore() + contentBoxLogicalHeight;
1812 if (auto selfCollapsingMarginBeforeWithClear = this->selfCollapsingMarginBeforeWithClear(lastChild()))
1813 borderBoxLogicalHeight -= *selfCollapsingMarginBeforeWithClear;
1814
1815 // If we can't collapse with children then add in the bottom margin.
1816 if (!marginInfo.canCollapseWithMarginAfter() && !marginInfo.canCollapseWithMarginBefore()
1817 && (!document().inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.hasMarginAfterQuirk())) {
1818 borderBoxLogicalHeight += marginInfo.margin();
1819 }
1820
1821 // Now add in our bottom border/padding.
1822 borderBoxLogicalHeight += borderAndPaddingAfter() + scrollbarLogicalHeight();
1823
1824 // Negative margins can cause our height to shrink below our minimal height (border/padding).
1825 // If this happens, ensure that the computed height is increased to the minimal height.
1826 borderBoxLogicalHeight = std::max(borderBoxLogicalHeight, borderAndPaddingLogicalHeight() + scrollbarLogicalHeight());
1827
1828 // Update our bottom collapsed margin info.
1829 setCollapsedBottomMargin(marginInfo);
1830
1831 return borderBoxLogicalHeight;
1832}
1833
1834void RenderBlockFlow::setMaxMarginBeforeValues(LayoutUnit pos, LayoutUnit neg)
1835{
1836 if (!hasRareBlockFlowData()) {
1837 if (pos == RenderBlockFlowRareData::positiveMarginBeforeDefault(*this) && neg == RenderBlockFlowRareData::negativeMarginBeforeDefault(*this))
1838 return;
1839 materializeRareBlockFlowData();
1840 }
1841
1842 rareBlockFlowData()->m_margins.setPositiveMarginBefore(pos);
1843 rareBlockFlowData()->m_margins.setNegativeMarginBefore(neg);
1844}
1845
1846void RenderBlockFlow::setMaxMarginAfterValues(LayoutUnit pos, LayoutUnit neg)
1847{
1848 if (!hasRareBlockFlowData()) {
1849 if (pos == RenderBlockFlowRareData::positiveMarginAfterDefault(*this) && neg == RenderBlockFlowRareData::negativeMarginAfterDefault(*this))
1850 return;
1851 materializeRareBlockFlowData();
1852 }
1853
1854 rareBlockFlowData()->m_margins.setPositiveMarginAfter(pos);
1855 rareBlockFlowData()->m_margins.setNegativeMarginAfter(neg);
1856}
1857
1858static bool inNormalFlow(RenderBox& child)
1859{
1860 RenderBlock* curr = child.containingBlock();
1861 while (curr && curr != &child.view()) {
1862 if (curr->isRenderFragmentedFlow())
1863 return true;
1864 if (curr->isFloatingOrOutOfFlowPositioned())
1865 return false;
1866 curr = curr->containingBlock();
1867 }
1868 return true;
1869}
1870
1871LayoutUnit RenderBlockFlow::applyBeforeBreak(RenderBox& child, LayoutUnit logicalOffset)
1872{
1873 // FIXME: Add page break checking here when we support printing.
1874 CheckedPtr fragmentedFlow = enclosingFragmentedFlow();
1875 bool isInsideMulticolFlow = !!fragmentedFlow;
1876 bool checkColumnBreaks = fragmentedFlow && fragmentedFlow->shouldCheckColumnBreaks() && (!shouldApplyLayoutContainment() || child.previousSibling());
1877 bool checkPageBreaks = !checkColumnBreaks && view().frameView().layoutContext().layoutState()->pageLogicalHeight(); // FIXME: Once columns can print we have to check this.
1878 bool checkFragmentBreaks = false;
1879 bool checkBeforeAlways = (checkColumnBreaks && child.style().breakBefore() == BreakBetween::Column)
1880 || (checkPageBreaks && alwaysPageBreak(child.style().breakBefore()));
1881 if (checkBeforeAlways && inNormalFlow(child) && hasNextPage(logicalOffset, IncludePageBoundary)) {
1882 if (checkColumnBreaks) {
1883 if (isInsideMulticolFlow)
1884 checkFragmentBreaks = true;
1885 }
1886 if (checkFragmentBreaks) {
1887 LayoutUnit offsetBreakAdjustment;
1888 if (fragmentedFlow->addForcedFragmentBreak(this, offsetFromLogicalTopOfFirstPage() + logicalOffset, &child, true, &offsetBreakAdjustment))
1889 return logicalOffset + offsetBreakAdjustment;
1890 }
1891 return nextPageLogicalTop(logicalOffset, IncludePageBoundary);
1892 }
1893 return logicalOffset;
1894}
1895
1896LayoutUnit RenderBlockFlow::applyAfterBreak(RenderBox& child, LayoutUnit logicalOffset, MarginInfo& marginInfo)
1897{
1898 // FIXME: Add page break checking here when we support printing.
1899 CheckedPtr fragmentedFlow = enclosingFragmentedFlow();
1900 bool isInsideMulticolFlow = !!fragmentedFlow;
1901 bool checkColumnBreaks = fragmentedFlow && fragmentedFlow->shouldCheckColumnBreaks();
1902 bool checkPageBreaks = !checkColumnBreaks && view().frameView().layoutContext().layoutState()->pageLogicalHeight(); // FIXME: Once columns can print we have to check this.
1903 bool checkFragmentBreaks = false;
1904 bool checkAfterAlways = (checkColumnBreaks && child.style().breakAfter() == BreakBetween::Column)
1905 || (checkPageBreaks && alwaysPageBreak(child.style().breakAfter()));
1906 if (checkAfterAlways && inNormalFlow(child) && hasNextPage(logicalOffset, IncludePageBoundary)) {
1907
1908 // So our margin doesn't participate in the next collapsing steps.
1909 marginInfo.clearMargin();
1910
1911 if (checkColumnBreaks) {
1912 if (isInsideMulticolFlow)
1913 checkFragmentBreaks = true;
1914 }
1915 if (checkFragmentBreaks) {
1916 LayoutUnit offsetBreakAdjustment;
1917 if (fragmentedFlow->addForcedFragmentBreak(this, offsetFromLogicalTopOfFirstPage() + logicalOffset, &child, false, &offsetBreakAdjustment))
1918 return logicalOffset + offsetBreakAdjustment;
1919 }
1920 return nextPageLogicalTop(logicalOffset, IncludePageBoundary);
1921 }
1922 return logicalOffset;
1923}
1924
1925LayoutUnit RenderBlockFlow::adjustBlockChildForPagination(LayoutUnit logicalTopAfterClear, LayoutUnit estimateWithoutPagination, RenderBox& child, bool atBeforeSideOfBlock)
1926{
1927 auto* childRenderBlock = dynamicDowncast<RenderBlock>(child);
1928
1929 if (estimateWithoutPagination != logicalTopAfterClear) {
1930 // Our guess prior to pagination movement was wrong. Before we attempt to paginate, let's try again at the new
1931 // position.
1932 setLogicalHeight(logicalTopAfterClear);
1933 setLogicalTopForChild(child, logicalTopAfterClear, ApplyLayoutDelta);
1934
1935 if (child.shrinkToAvoidFloats()) {
1936 // The child's width depends on the line width. When the child shifts to clear an item, its width can
1937 // change (because it has more available line width). So mark the item as dirty.
1938 child.setChildNeedsLayout(MarkOnlyThis);
1939 }
1940
1941 if (childRenderBlock) {
1942 if (!child.avoidsFloats() && childRenderBlock->containsFloats())
1943 downcast<RenderBlockFlow>(*childRenderBlock).markAllDescendantsWithFloatsForLayout();
1944 child.markForPaginationRelayoutIfNeeded();
1945 }
1946
1947 // Our guess was wrong. Make the child lay itself out again.
1948 child.layoutIfNeeded();
1949 }
1950
1951 LayoutUnit oldTop = logicalTopAfterClear;
1952
1953 // If the object has a page or column break value of "before", then we should shift to the top of the next page.
1954 LayoutUnit result = applyBeforeBreak(child, logicalTopAfterClear);
1955
1956 if (child.shouldApplySizeContainment())
1957 adjustSizeContainmentChildForPagination(child, result);
1958
1959 // For replaced elements and scrolled elements, we want to shift them to the next page if they don't fit on the current one.
1960 LayoutUnit logicalTopBeforeUnsplittableAdjustment = result;
1961 LayoutUnit logicalTopAfterUnsplittableAdjustment = adjustForUnsplittableChild(child, result);
1962
1963 LayoutUnit paginationStrut;
1964 LayoutUnit unsplittableAdjustmentDelta = logicalTopAfterUnsplittableAdjustment - logicalTopBeforeUnsplittableAdjustment;
1965 LayoutUnit childLogicalHeight = child.logicalHeight();
1966 if (unsplittableAdjustmentDelta) {
1967 setPageBreak(result, childLogicalHeight - unsplittableAdjustmentDelta);
1968 paginationStrut = unsplittableAdjustmentDelta;
1969 } else if (childRenderBlock && childRenderBlock->paginationStrut())
1970 paginationStrut = childRenderBlock->paginationStrut();
1971
1972 if (paginationStrut) {
1973 // We are willing to propagate out to our parent block as long as we were at the top of the block prior
1974 // to collapsing our margins, and as long as we didn't clear or move as a result of other pagination.
1975 if (atBeforeSideOfBlock && oldTop == result && !isOutOfFlowPositioned() && !isRenderTableCell()) {
1976 // FIXME: Should really check if we're exceeding the page height before propagating the strut, but we don't
1977 // have all the information to do so (the strut only has the remaining amount to push). Gecko gets this wrong too
1978 // and pushes to the next page anyway, so not too concerned about it.
1979 setPaginationStrut(result + paginationStrut);
1980 if (childRenderBlock)
1981 childRenderBlock->setPaginationStrut(0);
1982 } else
1983 result += paginationStrut;
1984 }
1985
1986 if (!unsplittableAdjustmentDelta) {
1987 if (LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(result)) {
1988 LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(result, ExcludePageBoundary);
1989 LayoutUnit spaceShortage = child.logicalHeight() - remainingLogicalHeight;
1990 if (spaceShortage > 0) {
1991 // If the child crosses a column boundary, report a break, in case nothing inside it
1992 // has already done so. The column balancer needs to know how much it has to stretch
1993 // the columns to make more content fit. If no breaks are reported (but do occur),
1994 // the balancer will have no clue. Only measure the space after the last column
1995 // boundary, in case it crosses more than one.
1996 LayoutUnit spaceShortageInLastColumn = intMod(spaceShortage, pageLogicalHeight);
1997 setPageBreak(result, spaceShortageInLastColumn ? spaceShortageInLastColumn : spaceShortage);
1998 } else if (remainingLogicalHeight == pageLogicalHeight && offsetFromLogicalTopOfFirstPage() + child.logicalTop()) {
1999 // We're at the very top of a page or column, and it's not the first one. This child
2000 // may turn out to be the smallest piece of content that causes a page break, so we
2001 // need to report it.
2002 setPageBreak(result, childLogicalHeight);
2003 }
2004 }
2005 }
2006
2007 // Similar to how we apply clearance. Boost height() to be the place where we're going to position the child.
2008 setLogicalHeight(logicalHeight() + (result - oldTop));
2009
2010 // Return the final adjusted logical top.
2011 return result;
2012}
2013
2014static inline LayoutUnit calculateMinimumPageHeight(const RenderStyle& renderStyle, const InlineIterator::LineBoxIterator& lastLine, LayoutUnit lineTop, LayoutUnit lineBottom)
2015{
2016 // We may require a certain minimum number of lines per page in order to satisfy
2017 // orphans and widows, and that may affect the minimum page height.
2018 unsigned lineCount = std::max<unsigned>(renderStyle.orphans().tryValue().value_or(1).value, renderStyle.widows().tryValue().value_or(1).value);
2019 if (lineCount > 1) {
2020 auto line = lastLine;
2021 for (unsigned i = 1; i < lineCount && line->previous(); i++)
2022 line = line->previous();
2023
2024 // FIXME: Paginating using line overflow isn't all fine. See FIXME in
2025 // adjustLinePositionForPagination() for more details.
2026 lineTop = std::min(line->logicalTop(), line->inkOverflowLogicalTop());
2027 }
2028 return lineBottom - lineTop;
2029}
2030
2031static inline bool needsAppleMailPaginationQuirk(const RenderBlockFlow& renderer)
2032{
2033 if (!renderer.settings().appleMailPaginationQuirkEnabled())
2034 return false;
2035
2036 RefPtr element = renderer.element();
2037 if (element && element->idForStyleResolution() == "messageContentContainer"_s)
2038 return true;
2039
2040 return false;
2041}
2042
2043static void NODELETE[[clang::annotate_type("webkit.nodelete")]] clearShouldBreakAtLineToAvoidWidowIfNeeded(RenderBlockFlow& blockFlow)
2044{
2045 if (!blockFlow.shouldBreakAtLineToAvoidWidow())
2046 return;
2047 blockFlow.clearShouldBreakAtLineToAvoidWidow();
2048 blockFlow.setDidBreakAtLineToAvoidWidow();
2049}
2050
2051RenderBlockFlow::LinePaginationAdjustment RenderBlockFlow::computeLineAdjustmentForPagination(const InlineIterator::LineBoxIterator& lineBox, LayoutUnit delta, LayoutUnit floatMinimumBottom)
2052{
2053 // In blocks-in-inline case the nested block has been adjusted already by the block layout code.
2054 bool isBlockInInline = lineBox->lineLeftmostLeafBox() && lineBox->lineLeftmostLeafBox()->isBlockLevelBox();
2055 if (isBlockInInline) {
2056 clearShouldBreakAtLineToAvoidWidowIfNeeded(*this);
2057 return { };
2058 }
2059
2060 auto computeLeafBoxTopAndBottom = [&] {
2061 auto lineTop = LayoutUnit::max();
2062 auto lineBottom = LayoutUnit::min();
2063 for (auto box = lineBox->lineLeftmostLeafBox(); box; box.traverseLineRightwardOnLine()) {
2064 if (box->logicalTop() < lineTop)
2065 lineTop = box->logicalTop();
2066 if (box->logicalBottom() > lineBottom)
2067 lineBottom = box->logicalBottom();
2068 }
2069 return std::pair { lineTop, lineBottom };
2070 };
2071
2072 auto logicalOverflowTop = LayoutUnit { lineBox->inkOverflowLogicalTop() };
2073 auto logicalOverflowBottom = LayoutUnit { lineBox->inkOverflowLogicalBottom() };
2074 auto logicalOverflowHeight = logicalOverflowBottom - logicalOverflowTop;
2075 auto logicalTop = LayoutUnit { lineBox->logicalTop() };
2076 auto logicalOffset = std::min(logicalTop, logicalOverflowTop);
2077
2078 if (floatMinimumBottom) {
2079 // Don't push a float to the next page if it is taller than the page.
2080 auto floatHeight = floatMinimumBottom - logicalTop;
2081 if (floatHeight > pageLogicalHeightForOffset(floatMinimumBottom))
2082 floatMinimumBottom = 0_lu;
2083 }
2084
2085 auto logicalBottom = std::max(std::max(LayoutUnit { lineBox->logicalBottom() }, logicalOverflowBottom), floatMinimumBottom);
2086 auto lineHeight = logicalBottom - logicalOffset;
2087
2088 updateMinimumPageHeight(logicalOffset, calculateMinimumPageHeight(style(), lineBox, logicalOffset, logicalBottom));
2089 logicalOffset += delta;
2090
2091 LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset);
2092
2093 if (!pageLogicalHeight || !hasNextPage(logicalOffset)) {
2094 clearShouldBreakAtLineToAvoidWidowIfNeeded(*this);
2095 return { };
2096 }
2097
2098 CheckedPtr fragmentedFlow = enclosingFragmentedFlow();
2099 bool hasUniformPageLogicalHeight = !fragmentedFlow || fragmentedFlow->fragmentsHaveUniformLogicalHeight();
2100
2101 if (hasUniformPageLogicalHeight && logicalOverflowHeight > pageLogicalHeight) {
2102 // We are so tall that we are bigger than a page. Before we give up and just leave the line where it is, try drilling into the
2103 // line and computing a new height that excludes anything we consider "blank space". We will discard margins, descent, and even overflow. If we are
2104 // able to fit with the blank space and overflow excluded, we will give the line its own page with the highest non-blank element being aligned with the
2105 // top of the page.
2106 // FIXME: We are still honoring gigantic margins, which does leave open the possibility of blank pages caused by this heuristic. It remains to be seen whether or not
2107 // this will be a real-world issue. For now we don't try to deal with this problem.
2108 std::tie(logicalOffset, logicalBottom) = computeLeafBoxTopAndBottom();
2109 lineHeight = logicalBottom - logicalOffset;
2110 if (logicalOffset == LayoutUnit::max() || lineHeight > pageLogicalHeight) {
2111 // Give up. We're genuinely too big even after excluding blank space and overflow.
2112 clearShouldBreakAtLineToAvoidWidowIfNeeded(*this);
2113 return { };
2114 }
2115 pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset);
2116 }
2117
2118 LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset, ExcludePageBoundary);
2119
2120 int lineNumber = lineBox->lineIndex() + 1;
2121 if (remainingLogicalHeight < lineHeight || (shouldBreakAtLineToAvoidWidow() && lineBreakToAvoidWidow() == lineNumber)) {
2122 if (lineBreakToAvoidWidow() == lineNumber)
2123 clearShouldBreakAtLineToAvoidWidowIfNeeded(*this);
2124 // If we have a non-uniform page height, then we have to shift further possibly.
2125 if (!hasUniformPageLogicalHeight && !pushToNextPageWithMinimumLogicalHeight(remainingLogicalHeight, logicalOffset, lineHeight))
2126 return { };
2127 if (lineHeight > pageLogicalHeight) {
2128 // Split the top margin in order to avoid splitting the visible part of the line.
2129 remainingLogicalHeight -= std::min(lineHeight - pageLogicalHeight, std::max(0_lu, logicalOverflowTop - logicalTop));
2130 }
2131 LayoutUnit totalLogicalHeight = lineHeight + std::max<LayoutUnit>(0, logicalOffset);
2132 LayoutUnit pageLogicalHeightAtNewOffset = hasUniformPageLogicalHeight ? pageLogicalHeight : pageLogicalHeightForOffset(logicalOffset + remainingLogicalHeight);
2133
2134 setPageBreak(logicalOffset, lineHeight - remainingLogicalHeight);
2135
2136 bool avoidFirstLinePageBreak = lineBox->isFirst() && totalLogicalHeight < pageLogicalHeightAtNewOffset && !floatMinimumBottom;
2137 auto orphansValue = style().orphans().tryValue();
2138 bool affectedByOrphans = orphansValue && orphansValue->value >= lineNumber;
2139
2140 if ((avoidFirstLinePageBreak || affectedByOrphans) && !isOutOfFlowPositioned() && !isRenderTableCell()) {
2141 if (needsAppleMailPaginationQuirk(*this))
2142 return { };
2143
2144 auto firstLineBox = InlineIterator::firstLineBoxFor(*this);
2145 auto firstLineBoxOverflowTop = LayoutUnit { firstLineBox ? firstLineBox->inkOverflowLogicalTop() : 0 };
2146 auto firstLineUpperOverhang = std::max(-firstLineBoxOverflowTop, 0_lu);
2147 setPaginationStrut(remainingLogicalHeight + logicalOffset + firstLineUpperOverhang);
2148
2149 return { };
2150 }
2151
2152 return { remainingLogicalHeight, true };
2153 }
2154
2155 if (remainingLogicalHeight == pageLogicalHeight) {
2156 // We're at the very top of a page or column.
2157 bool isFirstLine = lineBox->isFirst();
2158 if (!isFirstLine || offsetFromLogicalTopOfFirstPage())
2159 setPageBreak(logicalOffset, lineHeight);
2160
2161 return { 0_lu, !isFirstLine };
2162 }
2163
2164 return { };
2165}
2166
2167void RenderBlockFlow::setBreakAtLineToAvoidWidow(int lineToBreak)
2168{
2169 ASSERT(lineToBreak >= 0)((void)0);
2170 ASSERT(!ensureRareBlockFlowData().m_didBreakAtLineToAvoidWidow)((void)0);
2171 ensureRareBlockFlowData().m_lineBreakToAvoidWidow = lineToBreak;
2172}
2173
2174void RenderBlockFlow::setDidBreakAtLineToAvoidWidow()
2175{
2176 ASSERT(!shouldBreakAtLineToAvoidWidow())((void)0);
2177 if (!hasRareBlockFlowData())
2178 return;
2179
2180 rareBlockFlowData()->m_didBreakAtLineToAvoidWidow = true;
2181}
2182
2183void RenderBlockFlow::clearDidBreakAtLineToAvoidWidow()
2184{
2185 if (!hasRareBlockFlowData())
2186 return;
2187
2188 rareBlockFlowData()->m_didBreakAtLineToAvoidWidow = false;
2189}
2190
2191void RenderBlockFlow::clearShouldBreakAtLineToAvoidWidow() const
2192{
2193 ASSERT(shouldBreakAtLineToAvoidWidow())((void)0);
2194 if (!hasRareBlockFlowData())
2195 return;
2196
2197 rareBlockFlowData()->m_lineBreakToAvoidWidow = -1;
2198}
2199
2200bool RenderBlockFlow::hasNextPage(LayoutUnit logicalOffset, PageBoundaryRule pageBoundaryRule) const
2201{
2202 ASSERT(view().frameView().layoutContext().layoutState() && view().frameView().layoutContext().layoutState()->isPaginated())((void)0);
2203
2204 CheckedPtr fragmentedFlow = enclosingFragmentedFlow();
2205 if (!fragmentedFlow)
2206 return true; // Printing and multi-column both make new pages to accommodate content.
2207
2208 // See if we're in the last fragment.
2209 LayoutUnit pageOffset = offsetFromLogicalTopOfFirstPage() + logicalOffset;
2210 RenderFragmentContainer* fragment = fragmentedFlow->fragmentAtBlockOffset(this, pageOffset, true);
2211 if (!fragment)
2212 return false;
2213
2214 if (fragment->isLastFragment())
2215 return fragment->isRenderFragmentContainerSet() || (pageBoundaryRule == IncludePageBoundary && pageOffset == fragment->logicalTopForFragmentedFlowContent());
2216
2217 RenderFragmentContainer* startFragment = nullptr;
2218 RenderFragmentContainer* endFragment = nullptr;
2219 fragmentedFlow->getFragmentRangeForBox(*this, startFragment, endFragment);
2220 return (endFragment && fragment != endFragment);
2221}
2222
2223LayoutUnit RenderBlockFlow::adjustForUnsplittableChild(RenderBox& child, LayoutUnit logicalOffset, LayoutUnit childBeforeMargin, LayoutUnit childAfterMargin)
2224{
2225 // When flexboxes are embedded inside a block flow, they don't perform any adjustments for unsplittable
2226 // children. We'll treat flexboxes themselves as unsplittable just to get them to paginate properly inside
2227 // a block flow.
2228 bool isUnsplittable = childBoxIsUnsplittableForFragmentation(child);
2229 if (!isUnsplittable) {
2230 auto* flexibleBox = dynamicDowncast<RenderFlexibleBox>(child);
2231 if (!(flexibleBox && !flexibleBox->isFlexibleBoxImpl()))
2232 return logicalOffset;
2233 }
2234
2235 CheckedPtr fragmentedFlow = enclosingFragmentedFlow();
2236 LayoutUnit childLogicalHeight = logicalHeightForChild(child) + childBeforeMargin + childAfterMargin;
2237 LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset);
2238 bool hasUniformPageLogicalHeight = !fragmentedFlow || fragmentedFlow->fragmentsHaveUniformLogicalHeight();
2239 if (isUnsplittable)
2240 updateMinimumPageHeight(logicalOffset, childLogicalHeight);
2241 if (!pageLogicalHeight || (hasUniformPageLogicalHeight && childLogicalHeight > pageLogicalHeight)
2242 || !hasNextPage(logicalOffset))
2243 return logicalOffset;
2244 LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset, ExcludePageBoundary);
2245 if (remainingLogicalHeight < childLogicalHeight) {
2246 if (!hasUniformPageLogicalHeight && !pushToNextPageWithMinimumLogicalHeight(remainingLogicalHeight, logicalOffset, childLogicalHeight))
2247 return logicalOffset;
2248 auto result = logicalOffset + remainingLogicalHeight;
2249 bool isInitialLetter = child.isFloating() && child.style().pseudoElementType() == PseudoElementType::FirstLetter && child.style().initialLetter().drop() > 0;
2250 if (isInitialLetter) {
2251 // Increase our logical height to ensure that lines all get pushed along with the letter.
2252 setLogicalHeight(logicalOffset + remainingLogicalHeight);
2253 }
2254 return result;
2255 }
2256
2257 return logicalOffset;
2258}
2259
2260bool RenderBlockFlow::pushToNextPageWithMinimumLogicalHeight(LayoutUnit& adjustment, LayoutUnit logicalOffset, LayoutUnit minimumLogicalHeight) const
2261{
2262 bool checkFragment = false;
2263 auto* fragmentedFlow = enclosingFragmentedFlow();
2264 RenderFragmentContainer* currentFragmentContainer = nullptr;
2265 for (auto pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset + adjustment); pageLogicalHeight; pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset + adjustment)) {
2266 if (minimumLogicalHeight <= pageLogicalHeight)
2267 return true;
2268 auto adjustedOffset = logicalOffset + adjustment;
2269 if (!hasNextPage(adjustedOffset))
2270 return false;
2271 if (fragmentedFlow) {
2272 // While in layout and the columnsets are not balanced yet, we keep finding the same (infinite tall) column over and over again.
2273 auto* nextFragmentContainer = fragmentedFlow->fragmentAtBlockOffset(this, adjustedOffset, true);
2274 ASSERT(nextFragmentContainer)((void)0);
2275 if (nextFragmentContainer == currentFragmentContainer)
2276 return false;
2277 currentFragmentContainer = nextFragmentContainer;
2278 }
2279 adjustment += pageLogicalHeight;
2280 checkFragment = true;
2281 }
2282 return !checkFragment;
2283}
2284
2285void RenderBlockFlow::setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage)
2286{
2287 if (CheckedPtr fragmentedFlow = enclosingFragmentedFlow())
2288 fragmentedFlow->setPageBreak(this, offsetFromLogicalTopOfFirstPage() + offset, spaceShortage);
2289}
2290
2291void RenderBlockFlow::updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minHeight)
2292{
2293 if (CheckedPtr fragmentedFlow = enclosingFragmentedFlow())
2294 fragmentedFlow->updateMinimumPageHeight(this, offsetFromLogicalTopOfFirstPage() + offset, minHeight);
2295}
2296
2297LayoutUnit RenderBlockFlow::nextPageLogicalTop(LayoutUnit logicalOffset, PageBoundaryRule pageBoundaryRule) const
2298{
2299 LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset);
2300 if (!pageLogicalHeight)
2301 return logicalOffset;
2302
2303 // The logicalOffset is in our coordinate space. We can add in our pushed offset.
2304 LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset);
2305 if (pageBoundaryRule == ExcludePageBoundary)
2306 return logicalOffset + (remainingLogicalHeight ? remainingLogicalHeight : pageLogicalHeight);
2307 return logicalOffset + remainingLogicalHeight;
2308}
2309
2310LayoutUnit RenderBlockFlow::pageLogicalTopForOffset(LayoutUnit offset) const
2311{
2312 // Unsplittable objects clear out the pageLogicalHeight in the layout state as a way of signaling that no
2313 // pagination should occur. Therefore we have to check this first and bail if the value has been set to 0.
2314 auto* layoutState = view().frameView().layoutContext().layoutState();
2315 LayoutUnit pageLogicalHeight = layoutState->pageLogicalHeight();
2316 if (!pageLogicalHeight)
2317 return 0;
2318
2319 LayoutUnit firstPageLogicalTop = isHorizontalWritingMode() ? layoutState->pageOffset().height() : layoutState->pageOffset().width();
2320 LayoutUnit blockLogicalTop = isHorizontalWritingMode() ? layoutState->layoutOffset().height() : layoutState->layoutOffset().width();
2321
2322 LayoutUnit cumulativeOffset = offset + blockLogicalTop;
2323 if (CheckedPtr fragmentedFlow = enclosingFragmentedFlow())
2324 return firstPageLogicalTop + fragmentedFlow->pageLogicalTopForOffset(cumulativeOffset - firstPageLogicalTop);
2325
2326 return cumulativeOffset - roundToInt(cumulativeOffset - firstPageLogicalTop) % roundToInt(pageLogicalHeight);
2327}
2328
2329LayoutUnit RenderBlockFlow::pageLogicalHeightForOffset(LayoutUnit offset) const
2330{
2331 // Unsplittable objects clear out the pageLogicalHeight in the layout state as a way of signaling that no
2332 // pagination should occur. Therefore we have to check this first and bail if the value has been set to 0.
2333 LayoutUnit pageLogicalHeight = view().frameView().layoutContext().layoutState()->pageLogicalHeight();
2334 if (!pageLogicalHeight)
2335 return 0;
2336
2337 // Now check for a flow thread.
2338 if (CheckedPtr fragmentedFlow = enclosingFragmentedFlow())
2339 return fragmentedFlow->pageLogicalHeightForOffset(offset + offsetFromLogicalTopOfFirstPage());
2340 return pageLogicalHeight;
2341}
2342
2343LayoutUnit RenderBlockFlow::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule) const
2344{
2345 offset += offsetFromLogicalTopOfFirstPage();
2346
2347 if (CheckedPtr fragmentedFlow = enclosingFragmentedFlow())
2348 return fragmentedFlow->pageRemainingLogicalHeightForOffset(offset, pageBoundaryRule);
2349
2350 LayoutUnit pageLogicalHeight = view().frameView().layoutContext().layoutState()->pageLogicalHeight();
2351 LayoutUnit remainingHeight = pageLogicalHeight - intMod(offset, pageLogicalHeight);
2352 if (pageBoundaryRule == IncludePageBoundary) {
2353 // If includeBoundaryPoint is true the line exactly on the top edge of a
2354 // column will act as being part of the previous column.
2355 remainingHeight = intMod(remainingHeight, pageLogicalHeight);
2356 }
2357 return remainingHeight;
2358}
2359
2360LayoutUnit RenderBlockFlow::logicalHeightForChildForFragmentation(const RenderBox& child) const
2361{
2362 return logicalHeightForChild(child);
2363}
2364
2365void RenderBlockFlow::adjustSizeContainmentChildForPagination(RenderBox& child, LayoutUnit offset)
2366{
2367 if (!child.shouldApplySizeContainment())
2368 return;
2369
2370 LayoutUnit childOverflowHeight = child.isHorizontalWritingMode() ? child.layoutOverflowRect().maxY() : child.layoutOverflowRect().maxX();
2371 LayoutUnit childLogicalHeight = std::max(child.logicalHeight(), childOverflowHeight);
2372
2373 LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(offset, ExcludePageBoundary);
2374
2375 LayoutUnit spaceShortage = childLogicalHeight - remainingLogicalHeight;
2376 if (spaceShortage <= 0)
2377 return;
2378
2379 if (CheckedPtr fragmentedFlow = enclosingFragmentedFlow())
2380 fragmentedFlow->updateSpaceShortageForSizeContainment(this, offsetFromLogicalTopOfFirstPage() + offset, spaceShortage);
2381}
2382
2383bool RenderBlockFlow::containsFloats() const
2384{
2385 return m_floatingObjects && !m_floatingObjects->set().isEmpty();
2386}
2387
2388bool RenderBlockFlow::containsFloat(const RenderBox& renderer) const
2389{
2390 return m_floatingObjects && m_floatingObjects->set().contains<FloatingObjectHashTranslator>(renderer);
2391}
2392
2393bool RenderBlockFlow::subtreeContainsFloat(const RenderBox& renderer) const
2394{
2395 if (containsFloat(renderer))
2396 return true;
2397
2398 for (auto& blockFlow : descendantsOfType<RenderBlockFlow>(*this)) {
2399 if (blockFlow.containsFloat(renderer))
2400 return true;
2401 }
2402
2403 return false;
2404}
2405
2406bool RenderBlockFlow::subtreeContainsFloats() const
2407{
2408 if (containsFloats())
2409 return true;
2410
2411 for (auto& blockFlow : descendantsOfType<RenderBlockFlow>(*this)) {
2412 if (blockFlow.containsFloats())
2413 return true;
2414 }
2415
2416 return false;
2417}
2418
2419void RenderBlockFlow::styleDidChange(Style::Difference diff, const RenderStyle* oldStyle)
2420{
2421 RenderBlock::styleDidChange(diff, oldStyle);
2422
2423 // After our style changed, if we lose our ability to propagate floats into next sibling
2424 // blocks, then we need to find the top most parent containing that overhanging float and
2425 // then mark its descendants with floats for layout and clear all floats from its next
2426 // sibling blocks that exist in our floating objects list. See bug 56299 and 62875.
2427 bool canPropagateFloatIntoSibling = !isFloatingOrOutOfFlowPositioned() && !avoidsFloats();
2428 if (diff == Style::DifferenceResult::Layout && s_canPropagateFloatIntoSibling && !canPropagateFloatIntoSibling && hasOverhangingFloats()) {
2429 RenderBlockFlow* parentBlock = this;
2430 for (auto& ancestor : ancestorsOfType<RenderBlockFlow>(*this)) {
2431 if (ancestor.isRenderView())
2432 break;
2433 if (ancestor.hasOverhangingFloats()) {
2434 for (auto& floatingObject : m_floatingObjects->set()) {
2435 if (!floatingObject->renderer())
2436 continue;
2437 if (ancestor.hasOverhangingFloat(*floatingObject->renderer())) {
2438 parentBlock = &ancestor;
2439 break;
2440 }
2441 }
2442 }
2443 }
2444
2445 parentBlock->markAllDescendantsWithFloatsForLayout();
2446 parentBlock->markSiblingsWithFloatsForLayout();
2447 }
2448
2449 if (diff == Style::DifferenceResult::Layout && selfNeedsLayout() && childrenInline()) {
2450 for (auto walker = InlineWalker(*this); !walker.atEnd(); walker.advance())
2451 walker.current()->setNeedsPreferredWidthsUpdate();
2452 }
2453
2454 if (multiColumnFlow())
2455 updateStylesForColumnChildren(oldStyle);
2456}
2457
2458void RenderBlockFlow::updateStylesForColumnChildren(const RenderStyle* oldStyle)
2459{
2460 auto columnsNeedLayout = oldStyle && (oldStyle->columnCount() != style().columnCount() || oldStyle->columnWidth() != style().columnWidth());
2461 for (auto* child = firstChildBox(); child && (child->isRenderFragmentedFlow() || child->isRenderMultiColumnSet()); child = child->nextSiblingBox()) {
2462 child->setStyle(RenderStyle::createAnonymousStyleWithDisplay(style(), Style::DisplayType::BlockFlow));
2463 if (columnsNeedLayout)
2464 child->setNeedsLayoutAndPreferredWidthsUpdate();
2465 }
2466}
2467
2468void RenderBlockFlow::styleWillChange(Style::Difference diff, const RenderStyle& newStyle)
2469{
2470 const RenderStyle* oldStyle = hasInitializedStyle() ? &style() : nullptr;
2471 s_canPropagateFloatIntoSibling = oldStyle ? !isFloatingOrOutOfFlowPositioned() && !avoidsFloats() : false;
2472
2473 if (oldStyle) {
2474 auto oldPosition = oldStyle->position();
2475 auto newPosition = newStyle.position();
2476
2477 if (parent() && diff == Style::DifferenceResult::Layout && oldPosition != newPosition) {
2478 if (containsFloats() && !isFloating() && !isOutOfFlowPositioned() && newStyle.hasOutOfFlowPosition())
2479 markAllDescendantsWithFloatsForLayout();
2480 }
2481 }
2482
2483 RenderBlock::styleWillChange(diff, newStyle);
2484}
2485
2486void RenderBlockFlow::addFloatsToNewParent(RenderBlockFlow& toBlockFlow) const
2487{
2488 // When a portion of the render tree is being detached, anonymous blocks
2489 // will be combined as their children are deleted. In this process, the
2490 // anonymous block later in the tree is merged into the one preceeding it.
2491 // It can happen that the later block (this) contains floats that the
2492 // previous block (toBlockFlow) did not contain, and thus are not in the
2493 // floating objects list for toBlockFlow. This can result in toBlockFlow
2494 // containing floats that are not in its floating objects list, but are in
2495 // the floating objects lists of siblings and parents. This can cause
2496 // problems when the float itself is deleted, since the deletion code
2497 // assumes that if a float is not in its containing block's floating
2498 // objects list, it isn't in any floating objects list. In order to
2499 // preserve this condition (removing it has serious performance
2500 // implications), we need to copy the floating objects from the old block
2501 // (this) to the new block (toBlockFlow). The float's metrics will likely
2502 // all be wrong, but since toBlockFlow is already marked for layout, this
2503 // will get fixed before anything gets displayed.
2504 // See bug https://bugs.webkit.org/show_bug.cgi?id=115566
2505 if (!m_floatingObjects)
2506 return;
2507
2508 if (layoutContext().isSkippedContentForLayout(toBlockFlow))
2509 return;
2510
2511 if (!toBlockFlow.m_floatingObjects)
2512 toBlockFlow.createFloatingObjects();
2513
2514 for (auto& floatingObject : m_floatingObjects->set()) {
2515 if (!floatingObject->renderer())
2516 continue;
2517 if (toBlockFlow.containsFloat(*floatingObject->renderer()))
2518 continue;
2519 toBlockFlow.m_floatingObjects->add(floatingObject->cloneForNewParent());
2520 }
2521}
2522
2523void RenderBlockFlow::computeInFlowOverflow(LayoutRect contentArea, OptionSet<ComputeOverflowOptions> options)
2524{
2525 RenderBlock::computeInFlowOverflow(contentArea, options);
2526
2527 auto addOverflowFromFloatsIfApplicable = [&] {
2528
2529 if (!m_floatingObjects)
2530 return;
2531 auto shouldIncludeFloats = !multiColumnFlow() && (options.contains(ComputeOverflowOptions::RecomputeFloats) || createsNewFormattingContext() || hasSelfPaintingLayer());
2532 if (!shouldIncludeFloats)
2533 return;
2534
2535 for (auto& floatingObject : m_floatingObjects->set()) {
2536 if (floatingObject->isDescendant())
2537 addOverflowFromFloatBox(*floatingObject);
2538 }
2539 };
2540 addOverflowFromFloatsIfApplicable();
2541}
2542
2543void RenderBlockFlow::repaintOverhangingFloats(bool paintAllDescendants)
2544{
2545 // Repaint any overhanging floats (if we know we're the one to paint them).
2546 // Otherwise, bail out.
2547 if (!hasOverhangingFloats())
2548 return;
2549
2550 // FIXME: Avoid disabling LayoutState. At the very least, don't disable it for floats originating
2551 // in this block. Better yet would be to push extra state for the containers of other floats.
2552 LayoutStateDisabler layoutStateDisabler(view().frameView().layoutContext());
2553 for (auto& floatingObject : m_floatingObjects->set()) {
2554 if (!floatingObject->renderer())
2555 continue;
2556 // Only repaint the object if it is overhanging, is not in its own layer, and
2557 // is our responsibility to paint (m_shouldPaint is set). When paintAllDescendants is true, the latter
2558 // condition is replaced with being a descendant of us.
2559 auto& renderer = *floatingObject->renderer();
2560 if (logicalBottomForFloat(*floatingObject) > logicalHeight()
2561 && !renderer.hasSelfPaintingLayer()
2562 && (floatingObject->paintsFloat() || (paintAllDescendants && renderer.isDescendantOf(this)))) {
2563 renderer.repaint();
2564 renderer.repaintOverhangingFloats(false);
2565 }
2566 }
2567}
2568
2569void RenderBlockFlow::dirtyLineFromChangedChild()
2570{
2571 if (svgTextLayout() && svgTextLayout()->legacyRootBox())
2572 svgTextLayout()->legacyRootBox()->markDirty();
2573}
2574
2575void RenderBlockFlow::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& point)
2576{
2577 RenderBlock::paintColumnRules(paintInfo, point);
2578
2579 if (!multiColumnFlow() || paintInfo.context().paintingDisabled())
2580 return;
2581
2582 // Iterate over our children and paint the column rules as needed.
2583 for (auto& columnSet : childrenOfType<RenderMultiColumnSet>(*this)) {
2584 LayoutPoint childPoint = columnSet.location() + flipForWritingModeForChild(columnSet, point);
2585 columnSet.paintColumnRules(paintInfo, childPoint);
2586 }
2587}
2588
2589void RenderBlockFlow::paintFloats(PaintInfo& paintInfo, const LayoutPoint& paintOffset, bool preservePhase)
2590{
2591 if (!m_floatingObjects)
2592 return;
2593
2594 for (auto& floatingObject : m_floatingObjects->set()) {
2595 if (!floatingObject->renderer())
2596 continue;
2597 if (!floatingObject->shouldPaint())
2598 continue;
2599
2600 auto floatBoxLocation = flipFloatForWritingModeForChild(*floatingObject, paintOffset + floatingObject->translationOffsetToAncestor());
2601 if (preservePhase) {
2602 floatingObject->renderer()->paint(paintInfo, floatBoxLocation);
2603 continue;
2604 }
2605 auto& renderer = *floatingObject->renderer();
2606 auto paintInfoForFloat = PaintInfo { paintInfo };
2607
2608 paintInfoForFloat.phase = PaintPhase::BlockBackground;
2609 renderer.paint(paintInfoForFloat, floatBoxLocation);
2610
2611 paintInfoForFloat.phase = PaintPhase::ChildBlockBackgrounds;
2612 renderer.paint(paintInfoForFloat, floatBoxLocation);
2613
2614 paintInfoForFloat.phase = PaintPhase::Float;
2615 renderer.paint(paintInfoForFloat, floatBoxLocation);
2616
2617 paintInfoForFloat.phase = PaintPhase::Foreground;
2618 renderer.paint(paintInfoForFloat, floatBoxLocation);
2619
2620 paintInfoForFloat.phase = PaintPhase::Outline;
2621 renderer.paint(paintInfoForFloat, floatBoxLocation);
2622 }
2623}
2624
2625void RenderBlockFlow::clipOutFloatingBoxes(RenderBlock& rootBlock, const PaintInfo* paintInfo, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock)
2626{
2627 if (!m_floatingObjects)
2628 return;
2629
2630 for (auto& floatingObject : m_floatingObjects->set()) {
2631 if (!floatingObject->renderer())
2632 continue;
2633 LayoutRect floatBox(offsetFromRootBlock.width(), offsetFromRootBlock.height(), floatingObject->renderer()->width(), floatingObject->renderer()->height());
2634 floatBox.move(floatingObject->locationOffsetOfBorderBox());
2635 rootBlock.flipForWritingMode(floatBox);
2636 floatBox.move(rootBlockPhysicalPosition.x(), rootBlockPhysicalPosition.y());
2637 paintInfo->context().clipOut(snappedIntRect(floatBox));
2638 }
2639}
2640
2641void RenderBlockFlow::createFloatingObjects()
2642{
2643 m_floatingObjects = makeUnique<FloatingObjects>(*this);
2644}
2645
2646void RenderBlockFlow::removeFloatingObjects()
2647{
2648 if (!m_floatingObjects)
2649 return;
2650
2651 markSiblingsWithFloatsForLayout();
2652
2653 m_floatingObjects->clear();
2654}
2655
2656void RenderBlockFlow::insertFloatingBoxAndMarkForLayout(RenderBox& floatBox)
2657{
2658 // Our location is irrelevant if we're unsplittable or no pagination is in effect. Just lay out the float.
2659 bool isChildRenderBlock = floatBox.isRenderBlock();
2660 if (isChildRenderBlock && !floatBox.needsLayout() && view().frameView().layoutContext().layoutState()->pageLogicalHeightChanged())
2661 floatBox.setChildNeedsLayout(MarkOnlyThis);
2662
2663 bool needsBlockDirectionLocationSetBeforeLayout = isChildRenderBlock && view().frameView().layoutContext().layoutState()->needsBlockDirectionLocationSetBeforeLayout();
2664 if (!needsBlockDirectionLocationSetBeforeLayout || isWritingModeRoot()) {
2665 // We are unsplittable if we're a block flow root.
2666 floatBox.layoutIfNeeded();
2667 } else {
2668 floatBox.updateLogicalWidth();
2669 floatBox.computeAndSetBlockDirectionMargins(*this);
2670 }
2671
2672 auto& floatingObject = insertFloatingBox(floatBox);
2673 setLogicalWidthForFloat(floatingObject, logicalWidthForChild(floatBox) + marginStartForChild(floatBox) + marginEndForChild(floatBox));
2674}
2675
2676FloatingObject& RenderBlockFlow::insertFloatingBox(RenderBox& floatBox)
2677{
2678 ASSERT(floatBox.isFloating())((void)0);
2679 ASSERT(!layoutContext().isSkippedContentForLayout(*this))((void)0);
2680
2681 if (!m_floatingObjects)
2682 createFloatingObjects();
2683
2684 auto& floatingObjectSet = m_floatingObjects->set();
2685 auto it = floatingObjectSet.find<FloatingObjectHashTranslator>(floatBox);
2686 if (it != floatingObjectSet.end())
2687 return *it->get();
2688
2689 return *m_floatingObjects->add(FloatingObject::create(floatBox));
2690}
2691
2692void RenderBlockFlow::removeFloatingBox(RenderBox& floatBox)
2693{
2694 if (!m_floatingObjects)
2695 return;
2696
2697 auto& floatingObjectSet = m_floatingObjects->set();
2698 auto it = floatingObjectSet.find<FloatingObjectHashTranslator>(floatBox);
2699 if (it != floatingObjectSet.end())
2700 m_floatingObjects->remove(it->get());
2701}
2702
2703LayoutUnit RenderBlockFlow::logicalLeftOffsetForPositioningFloat(LayoutUnit logicalTop, LayoutUnit fixedOffset, LayoutUnit* heightRemaining) const
2704{
2705 LayoutUnit offset = fixedOffset;
2706 if (m_floatingObjects && m_floatingObjects->hasLeftObjects())
2707 offset = m_floatingObjects->logicalLeftOffsetForPositioningFloat(fixedOffset, logicalTop, heightRemaining);
2708 return adjustLogicalLeftOffsetForLine(offset);
2709}
2710
2711LayoutUnit RenderBlockFlow::logicalRightOffsetForPositioningFloat(LayoutUnit logicalTop, LayoutUnit fixedOffset, LayoutUnit* heightRemaining) const
2712{
2713 LayoutUnit offset = fixedOffset;
2714 if (m_floatingObjects && m_floatingObjects->hasRightObjects())
2715 offset = m_floatingObjects->logicalRightOffsetForPositioningFloat(fixedOffset, logicalTop, heightRemaining);
2716 return adjustLogicalRightOffsetForLine(offset);
2717}
2718
2719void RenderBlockFlow::computeLogicalLocationForFloat(FloatingObject& floatingObject, LayoutUnit& logicalTopOffset)
2720{
2721 if (!floatingObject.renderer())
2722 return;
2723
2724 auto& childBox = *floatingObject.renderer();
2725 LayoutUnit logicalLeftOffset = logicalLeftOffsetForContent(); // Constant part of left offset.
2726 LayoutUnit logicalRightOffset = logicalRightOffsetForContent(); // Constant part of right offset.
2727
2728 LayoutUnit floatLogicalWidth = std::min(logicalWidthForFloat(floatingObject), logicalRightOffset - logicalLeftOffset); // The width we look for.
2729
2730 LayoutUnit floatLogicalLeft;
2731
2732 bool insideFragmentedFlow = enclosingFragmentedFlow();
2733 bool isInitialLetter = childBox.style().pseudoElementType() == PseudoElementType::FirstLetter && childBox.style().initialLetter().drop() > 0;
2734
2735 if (isInitialLetter) {
2736 if (auto lowestInitialLetterLogicalBottom = this->lowestInitialLetterLogicalBottom()) {
2737 auto letterClearance = *lowestInitialLetterLogicalBottom - logicalTopOffset;
2738 if (letterClearance > 0) {
2739 logicalTopOffset += letterClearance;
2740 setLogicalHeight(logicalHeight() + letterClearance);
2741 }
2742 }
2743 }
2744
2745 if (RenderStyle::usedFloat(childBox) == UsedFloat::Left) {
2746 LayoutUnit heightRemainingLeft = 1_lu;
2747 LayoutUnit heightRemainingRight = 1_lu;
2748 floatLogicalLeft = logicalLeftOffsetForPositioningFloat(logicalTopOffset, logicalLeftOffset, &heightRemainingLeft);
2749 while (logicalRightOffsetForPositioningFloat(logicalTopOffset, logicalRightOffset, &heightRemainingRight) - floatLogicalLeft < floatLogicalWidth) {
2750 logicalTopOffset += std::min(heightRemainingLeft, heightRemainingRight);
2751 floatLogicalLeft = logicalLeftOffsetForPositioningFloat(logicalTopOffset, logicalLeftOffset, &heightRemainingLeft);
2752 if (insideFragmentedFlow) {
2753 // Have to re-evaluate all of our offsets, since they may have changed.
2754 logicalRightOffset = logicalRightOffsetForContent(); // Constant part of right offset.
2755 logicalLeftOffset = logicalLeftOffsetForContent(); // Constant part of left offset.
2756 floatLogicalWidth = std::min(logicalWidthForFloat(floatingObject), logicalRightOffset - logicalLeftOffset);
2757 }
2758 }
2759 floatLogicalLeft = std::max(logicalLeftOffset - borderAndPaddingLogicalLeft(), floatLogicalLeft);
2760 } else {
2761 LayoutUnit heightRemainingLeft = 1_lu;
2762 LayoutUnit heightRemainingRight = 1_lu;
2763 floatLogicalLeft = logicalRightOffsetForPositioningFloat(logicalTopOffset, logicalRightOffset, &heightRemainingRight);
2764 while (floatLogicalLeft - logicalLeftOffsetForPositioningFloat(logicalTopOffset, logicalLeftOffset, &heightRemainingLeft) < floatLogicalWidth) {
2765 logicalTopOffset += std::min(heightRemainingLeft, heightRemainingRight);
2766 floatLogicalLeft = logicalRightOffsetForPositioningFloat(logicalTopOffset, logicalRightOffset, &heightRemainingRight);
2767 if (insideFragmentedFlow) {
2768 // Have to re-evaluate all of our offsets, since they may have changed.
2769 logicalRightOffset = logicalRightOffsetForContent(); // Constant part of right offset.
2770 logicalLeftOffset = logicalLeftOffsetForContent(); // Constant part of left offset.
2771 floatLogicalWidth = std::min(logicalWidthForFloat(floatingObject), logicalRightOffset - logicalLeftOffset);
2772 }
2773 }
2774 // Use the original width of the float here, since the local variable
2775 // |floatLogicalWidth| was capped to the available line width. See
2776 // fast/block/float/clamped-right-float.html.
2777 floatLogicalLeft -= logicalWidthForFloat(floatingObject);
2778 }
2779
2780 LayoutUnit childLogicalLeftMargin = writingMode().isLogicalLeftInlineStart() ? marginStartForChild(childBox) : marginEndForChild(childBox);
2781 LayoutUnit childBeforeMargin = marginBeforeForChild(childBox);
2782
2783 if (isInitialLetter)
2784 adjustInitialLetterPosition(childBox, logicalTopOffset, childBeforeMargin);
2785
2786 setLogicalLeftForFloat(floatingObject, floatLogicalLeft);
2787 setLogicalLeftForChild(childBox, floatLogicalLeft + childLogicalLeftMargin);
2788
2789 setLogicalTopForFloat(floatingObject, logicalTopOffset);
2790 setLogicalTopForChild(childBox, logicalTopOffset + childBeforeMargin);
2791
2792 setLogicalMarginsForFloat(floatingObject, childLogicalLeftMargin, childBeforeMargin);
2793}
2794
2795void RenderBlockFlow::adjustInitialLetterPosition(RenderBox& childBox, LayoutUnit& logicalTopOffset, LayoutUnit& marginBeforeOffset)
2796{
2797 const RenderStyle& style = firstLineStyle();
2798 const FontMetrics& fontMetrics = style.metricsOfPrimaryFont();
2799 if (!fontMetrics.capHeight())
2800 return;
2801
2802 LayoutUnit heightOfLine = lineHeight();
2803 LayoutUnit beforeMarginBorderPadding = childBox.borderAndPaddingBefore() + childBox.marginBefore();
2804
2805 // Make an adjustment to align with the cap height of a theoretical block line.
2806 LayoutUnit adjustment = fontMetrics.intAscent() + (heightOfLine - fontMetrics.intHeight()) / 2 - fontMetrics.intCapHeight() - beforeMarginBorderPadding;
2807 logicalTopOffset += adjustment;
2808
2809 // For sunken and raised caps, we have to make some adjustments. Test if we're sunken or raised (dropHeightDelta will be
2810 // positive for raised and negative for sunken).
2811 int dropHeightDelta = childBox.style().initialLetter().height() - childBox.style().initialLetter().drop();
2812
2813 // If we're sunken, the float needs to shift down but lines still need to avoid it. In order to do that we increase the float's margin.
2814 if (dropHeightDelta < 0)
2815 marginBeforeOffset += -dropHeightDelta * heightOfLine;
2816
2817 // If we're raised, then we actually have to grow the height of the block, since the lines have to be pushed down as though we're placing
2818 // empty lines beside the first letter.
2819 if (dropHeightDelta > 0)
2820 setLogicalHeight(logicalHeight() + dropHeightDelta * heightOfLine);
2821}
2822
2823bool RenderBlockFlow::positionNewFloats()
2824{
2825 if (!m_floatingObjects)
2826 return false;
2827
2828 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
2829 if (floatingObjectSet.isEmpty())
2830 return false;
2831
2832 // If all floats have already been positioned, then we have no work to do.
2833 if (floatingObjectSet.last()->isPlaced())
2834 return false;
2835
2836 // Move backwards through our floating object list until we find a float that has
2837 // already been positioned. Then we'll be able to move forward, positioning all of
2838 // the new floats that need it.
2839 auto it = floatingObjectSet.end();
2840 --it; // Go to last item.
2841 auto begin = floatingObjectSet.begin();
2842 FloatingObject* lastPlacedFloatingObject = 0;
2843 while (it != begin) {
2844 --it;
2845 if ((*it)->isPlaced()) {
2846 lastPlacedFloatingObject = it->get();
2847 ++it;
2848 break;
2849 }
2850 }
2851
2852 LayoutUnit logicalTop = logicalHeight();
2853
2854 // The float cannot start above the top position of the last positioned float.
2855 if (lastPlacedFloatingObject)
2856 logicalTop = std::max(logicalTopForFloat(*lastPlacedFloatingObject), logicalTop);
2857
2858 auto end = floatingObjectSet.end();
2859 // Now walk through the set of unpositioned floats and place them.
2860 for (; it != end; ++it) {
2861 auto& floatingObject = *it->get();
2862 if (!floatingObject.renderer())
2863 continue;
2864 // The containing block is responsible for positioning floats, so if we have floats in our
2865 // list that come from somewhere else, do not attempt to position them.
2866 auto& childBox = *floatingObject.renderer();
2867 if (childBox.containingBlock() != this)
2868 continue;
2869
2870 LayoutRect oldRect = childBox.frameRect();
2871 auto childBoxUsedClear = RenderStyle::usedClear(childBox);
2872 if (childBoxUsedClear == UsedClear::Left || childBoxUsedClear == UsedClear::Both)
2873 logicalTop = std::max(lowestFloatLogicalBottom(FloatingObject::FloatLeft), logicalTop);
2874 if (childBoxUsedClear == UsedClear::Right || childBoxUsedClear == UsedClear::Both)
2875 logicalTop = std::max(lowestFloatLogicalBottom(FloatingObject::FloatRight), logicalTop);
2876
2877 computeLogicalLocationForFloat(floatingObject, logicalTop);
2878 LayoutUnit childLogicalTop = logicalTopForChild(childBox);
2879
2880 estimateFragmentRangeForBoxChild(childBox);
2881
2882 childBox.markForPaginationRelayoutIfNeeded();
2883 childBox.layoutIfNeeded();
2884
2885 auto* layoutState = view().frameView().layoutContext().layoutState();
2886 bool isPaginated = layoutState->isPaginated();
2887 if (isPaginated) {
2888 // If we are unsplittable and don't fit, then we need to move down.
2889 // We include our margins as part of the unsplittable area.
2890 LayoutUnit newLogicalTop = adjustForUnsplittableChild(childBox, logicalTop, childLogicalTop - logicalTop, marginAfterForChild(childBox));
2891
2892 // See if we have a pagination strut that is making us move down further.
2893 // Note that an unsplittable child can't also have a pagination strut, so this
2894 // is exclusive with the case above.
2895 auto* childBlock = dynamicDowncast<RenderBlock>(childBox);
2896 if (childBlock && childBlock->paginationStrut()) {
2897 newLogicalTop += childBlock->paginationStrut();
2898 childBlock->setPaginationStrut(0);
2899 }
2900
2901 if (newLogicalTop != logicalTop) {
2902 floatingObject.setPaginationStrut(newLogicalTop - logicalTop);
2903 computeLogicalLocationForFloat(floatingObject, newLogicalTop);
2904 if (childBlock)
2905 childBlock->setChildNeedsLayout(MarkOnlyThis);
2906 childBox.layoutIfNeeded();
2907 logicalTop = newLogicalTop;
2908 }
2909
2910 if (updateFragmentRangeForBoxChild(childBox)) {
2911 childBox.setNeedsLayout(MarkOnlyThis);
2912 childBox.layoutIfNeeded();
2913 }
2914 }
2915
2916 setLogicalHeightForFloat(floatingObject, logicalHeightForChildForFragmentation(childBox) + (logicalTopForChild(childBox) - logicalTop) + marginAfterForChild(childBox));
2917
2918 m_floatingObjects->addPlacedObject(&floatingObject);
2919
2920 if (ShapeOutsideInfo* shapeOutside = childBox.shapeOutsideInfo())
2921 shapeOutside->invalidateForSizeChangeIfNeeded();
2922 // If the child moved, we have to repaint it.
2923 if (childBox.checkForRepaintDuringLayout())
2924 childBox.repaintDuringLayoutIfMoved(oldRect);
2925 }
2926 return true;
2927}
2928
2929void RenderBlockFlow::clearFloats(UsedClear usedClear)
2930{
2931 positionNewFloats();
2932 // set y position
2933 LayoutUnit newY;
2934 switch (usedClear) {
2935 case UsedClear::Left:
2936 newY = lowestFloatLogicalBottom(FloatingObject::FloatLeft);
2937 break;
2938 case UsedClear::Right:
2939 newY = lowestFloatLogicalBottom(FloatingObject::FloatRight);
2940 break;
2941 case UsedClear::Both:
2942 newY = lowestFloatLogicalBottom();
2943 break;
2944 case UsedClear::None:
2945 break;
2946 }
2947 // FIXME: The float search tree has floored float box position (see FloatingObjects::intervalForFloatingObject).
2948 newY = newY.floor();
2949 if (height() < newY)
2950 setLogicalHeight(newY);
2951}
2952
2953LayoutUnit RenderBlockFlow::logicalLeftFloatOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, LayoutUnit logicalHeight) const
2954{
2955 if (m_floatingObjects && m_floatingObjects->hasLeftObjects())
2956 return m_floatingObjects->logicalLeftOffset(fixedOffset, logicalTop, logicalHeight);
2957
2958 return fixedOffset;
2959}
2960
2961LayoutUnit RenderBlockFlow::logicalRightFloatOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, LayoutUnit logicalHeight) const
2962{
2963 if (m_floatingObjects && m_floatingObjects->hasRightObjects())
2964 return m_floatingObjects->logicalRightOffset(fixedOffset, logicalTop, logicalHeight);
2965
2966 return fixedOffset;
2967}
2968
2969LayoutUnit RenderBlockFlow::nextFloatLogicalBottomBelow(LayoutUnit logicalHeight) const
2970{
2971 if (!m_floatingObjects)
2972 return logicalHeight;
2973
2974 return m_floatingObjects->findNextFloatLogicalBottomBelow(logicalHeight);
2975}
2976
2977LayoutUnit RenderBlockFlow::nextFloatLogicalBottomBelowForBlock(LayoutUnit logicalHeight) const
2978{
2979 if (!m_floatingObjects)
2980 return logicalHeight;
2981
2982 return m_floatingObjects->findNextFloatLogicalBottomBelowForBlock(logicalHeight);
2983}
2984
2985LayoutUnit RenderBlockFlow::lowestFloatLogicalBottom() const
2986{
2987 return lowestFloatLogicalBottom(FloatLeftRight);
2988}
2989
2990LayoutUnit RenderBlockFlow::lowestFloatLogicalBottom(FloatingObject::Type floatType) const
2991{
2992 if (!m_floatingObjects)
2993 return 0;
2994 LayoutUnit lowestFloatBottom;
2995 for (auto& floatingObject : m_floatingObjects->set()) {
2996 if (floatingObject->isPlaced() && floatingObject->type() & floatType)
2997 lowestFloatBottom = std::max(lowestFloatBottom, logicalBottomForFloat(*floatingObject));
2998 }
2999 return lowestFloatBottom;
3000}
3001
3002std::optional<LayoutUnit> RenderBlockFlow::lowestInitialLetterLogicalBottom() const
3003{
3004 if (!m_floatingObjects)
3005 return { };
3006 auto lowestFloatBottom = std::optional<LayoutUnit> { };
3007 for (auto& floatingObject : m_floatingObjects->set()) {
3008 if (!floatingObject->renderer())
3009 continue;
3010 if (floatingObject->isPlaced() && floatingObject->renderer()->style().pseudoElementType() == PseudoElementType::FirstLetter && floatingObject->renderer()->style().initialLetter().drop() > 0)
3011 lowestFloatBottom = std::max(lowestFloatBottom.value_or(0_lu), logicalBottomForFloat(*floatingObject));
3012 }
3013 return lowestFloatBottom;
3014}
3015
3016static RenderLayer* enclosingFloatPaintingLayer(const RenderBox& renderer)
3017{
3018 for (auto& box : lineageOfType<RenderBox>(renderer)) {
3019 if (box.layer() && box.layer()->isSelfPaintingLayer())
3020 return box.layer();
3021 }
3022 return { };
3023}
3024
3025LayoutUnit RenderBlockFlow::addOverhangingFloats(RenderBlockFlow& child, bool makeChildPaintOtherFloats)
3026{
3027 ASSERT(!layoutContext().isSkippedContentForLayout(*this))((void)0);
3028 // Prevent floats from being added to the canvas by the root element, e.g., <html>.
3029 if (!child.containsFloats() || child.createsNewFormattingContext())
3030 return 0;
3031
3032 LayoutUnit childLogicalTop = child.logicalTop();
3033 LayoutUnit childLogicalLeft = child.logicalLeft();
3034 LayoutUnit lowestFloatLogicalBottom;
3035
3036 // Floats that will remain the child's responsibility to paint should factor into its
3037 // overflow.
3038 auto blockHasOverflowClip = effectiveOverflowX() == Overflow::Clip || effectiveOverflowY() == Overflow::Clip;
3039 for (auto& floatingObject : child.m_floatingObjects->set()) {
3040 if (!floatingObject->renderer())
3041 continue;
3042 LayoutUnit floatLogicalBottom = std::min(logicalBottomForFloat(*floatingObject), LayoutUnit::max() - childLogicalTop);
3043 LayoutUnit logicalBottom = childLogicalTop + floatLogicalBottom;
3044 lowestFloatLogicalBottom = std::max(lowestFloatLogicalBottom, logicalBottom);
3045 CheckedRef renderer = *floatingObject->renderer();
3046
3047 if (logicalBottom > logicalHeight()) {
3048 // If the object is not in the list, we add it now.
3049 if (!containsFloat(renderer)) {
3050 LayoutSize offset = isHorizontalWritingMode() ? LayoutSize(-childLogicalLeft, -childLogicalTop) : LayoutSize(-childLogicalTop, -childLogicalLeft);
3051 bool shouldPaint = false;
3052
3053 // The nearest enclosing layer always paints the float (so that zindex and stacking
3054 // behaves properly). We always want to propagate the desire to paint the float as
3055 // far out as we can, to the outermost block that overlaps the float, stopping only
3056 // if we hit a self-painting layer boundary.
3057 if (!floatingObject->hasAncestorWithOverflowClip() && enclosingFloatPaintingLayer(renderer) == enclosingFloatPaintingLayer(*this)) {
3058 floatingObject->setPaintsFloat(false);
3059 shouldPaint = true;
3060 }
3061 // We create the floating object list lazily.
3062 if (!m_floatingObjects)
3063 createFloatingObjects();
3064
3065 m_floatingObjects->add(floatingObject->copyToNewContainer(offset, shouldPaint, true, floatingObject->hasAncestorWithOverflowClip() || blockHasOverflowClip));
3066 }
3067 } else {
3068 if (makeChildPaintOtherFloats && !floatingObject->paintsFloat() && !renderer->hasSelfPaintingLayer()
3069 && renderer->isDescendantOf(&child) && enclosingFloatPaintingLayer(renderer) == enclosingFloatPaintingLayer(child)) {
3070 // The float is not overhanging from this block, so if it is a descendant of the child, the child should
3071 // paint it (the other case is that it is intruding into the child), unless it has its own layer or enclosing
3072 // layer.
3073 // If makeChildPaintOtherFloats is false, it means that the child must already know about all the floats
3074 // it should paint.
3075 floatingObject->setPaintsFloat(true);
3076 }
3077
3078 // Since the float doesn't overhang, it didn't get put into our list. We need to add its overflow in to the child now.
3079 if (floatingObject->isDescendant())
3080 child.addOverflowFromFloatBox(*floatingObject);
3081 }
3082 }
3083 return lowestFloatLogicalBottom;
3084}
3085
3086bool RenderBlockFlow::hasOverhangingFloat(RenderBox& renderer)
3087{
3088 if (!m_floatingObjects || !parent())
3089 return false;
3090
3091 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
3092 const auto it = floatingObjectSet.find<FloatingObjectHashTranslator>(renderer);
3093 if (it == floatingObjectSet.end())
3094 return false;
3095
3096 return logicalBottomForFloat(*it->get()) > logicalHeight();
3097}
3098
3099void RenderBlockFlow::addIntrudingFloats(RenderBlockFlow* previousBlock, RenderBlockFlow* container, LayoutUnit logicalLeftOffset, LayoutUnit logicalTopOffset)
3100{
3101 ASSERT(!avoidsFloats())((void)0);
3102 ASSERT(!layoutContext().isSkippedContentForLayout(*this))((void)0);
3103
3104 // If we create our own block formatting context then our contents don't interact with floats outside it, even those from our parent.
3105 if (createsNewFormattingContext())
3106 return;
3107
3108 // If the parent or previous sibling doesn't have any floats to add, don't bother.
3109 if (!previousBlock->m_floatingObjects)
3110 return;
3111
3112 logicalLeftOffset += marginLogicalLeft();
3113
3114 for (auto& previousBlockFloatingObject : previousBlock->m_floatingObjects->set()) {
3115 if (!previousBlockFloatingObject->renderer())
3116 continue;
3117
3118 if (logicalBottomForFloat(*previousBlockFloatingObject) > logicalTopOffset) {
3119 if (!m_floatingObjects || !m_floatingObjects->set().contains(previousBlockFloatingObject)) {
3120 // We create the floating object list lazily.
3121 if (!m_floatingObjects)
3122 createFloatingObjects();
3123
3124 // Applying the child's margin makes no sense in the case where the child was passed in.
3125 // since this margin was added already through the modification of the |logicalLeftOffset| variable
3126 // above. |logicalLeftOffset| will equal the margin in this case, so it's already been taken
3127 // into account. Only apply this code if previousBlock is the parent, since otherwise the left margin
3128 // will get applied twice.
3129 LayoutSize offset = isHorizontalWritingMode()
3130 ? LayoutSize(logicalLeftOffset - (previousBlock != container ? previousBlock->marginLeft() : 0_lu), logicalTopOffset)
3131 : LayoutSize(logicalTopOffset, logicalLeftOffset - (previousBlock != container ? previousBlock->marginTop() : 0_lu));
3132
3133 m_floatingObjects->add(previousBlockFloatingObject->copyToNewContainer(offset));
3134 }
3135 }
3136 }
3137}
3138
3139void RenderBlockFlow::markAllDescendantsWithFloatsForLayout(RenderBox* floatToRemove, bool inLayout)
3140{
3141 if (!everHadLayout() && !containsFloats())
3142 return;
3143
3144 MarkingBehavior markParents = inLayout ? MarkOnlyThis : MarkContainingBlockChain;
3145 setChildNeedsLayout(markParents);
3146
3147 if (floatToRemove) {
3148 if (isSkippedContentRootOrSkippedContent(*this))
3149 clearNeedsLayout(HadSkippedLayout::Yes);
3150 else
3151 removeFloatingBox(*floatToRemove);
3152 } else if (childrenInline())
3153 return;
3154
3155 // Iterate over our block children and mark them as needed.
3156 for (auto walker = InlineWalker(*this); !walker.atEnd(); walker.advance()) {
3157 auto* block = dynamicDowncast<RenderBlock>(walker.current());
3158 if (!block)
3159 continue;
3160
3161 if (!floatToRemove && block->isFloatingOrOutOfFlowPositioned())
3162 continue;
3163 CheckedPtr blockFlow = dynamicDowncast<RenderBlockFlow>(*block);
3164 if (!blockFlow) {
3165 if (block->shrinkToAvoidFloats() && block->everHadLayout())
3166 block->setChildNeedsLayout(markParents);
3167 continue;
3168 }
3169 auto shouldCheckSubtree = isSkippedContentRootOrSkippedContent(*blockFlow)
3170 || (floatToRemove ? blockFlow->subtreeContainsFloat(*floatToRemove) : blockFlow->subtreeContainsFloats()) || blockFlow->shrinkToAvoidFloats();
3171 if (shouldCheckSubtree)
3172 blockFlow->markAllDescendantsWithFloatsForLayout(floatToRemove, inLayout);
3173 }
3174}
3175
3176void RenderBlockFlow::markSiblingsWithFloatsForLayout(RenderBox* floatToRemove)
3177{
3178 ASSERT(!floatToRemove || floatToRemove->isFloating())((void)0);
3179
3180 auto markSiblingsWithIntrusiveFloatForLayoutIfApplicable = [&](auto& floatBoxToRemove) {
3181 for (auto* nextSibling = this->nextSibling(); nextSibling; nextSibling = nextSibling->nextSibling()) {
3182 CheckedPtr nextSiblingBlockFlow = dynamicDowncast<RenderBlockFlow>(*nextSibling);
3183 if (!nextSiblingBlockFlow)
3184 continue;
3185 auto shouldCheckSubtree = isSkippedContentRootOrSkippedContent(*nextSiblingBlockFlow) || nextSiblingBlockFlow->containsFloat(floatBoxToRemove);
3186 if (shouldCheckSubtree)
3187 nextSiblingBlockFlow->markAllDescendantsWithFloatsForLayout(&floatBoxToRemove);
3188 }
3189 };
3190
3191 if (floatToRemove) {
3192 markSiblingsWithIntrusiveFloatForLayoutIfApplicable(*floatToRemove);
3193 return;
3194 }
3195
3196 if (!m_floatingObjects)
3197 return;
3198
3199 for (auto& floatingObject : m_floatingObjects->set()) {
3200 if (!floatingObject->renderer())
3201 continue;
3202 markSiblingsWithIntrusiveFloatForLayoutIfApplicable(*floatingObject->renderer());
3203 }
3204}
3205
3206LayoutPoint RenderBlockFlow::flipFloatForWritingModeForChild(const FloatingObject& child, const LayoutPoint& point) const
3207{
3208 if (!child.renderer())
3209 return point;
3210
3211 if (!writingMode().isBlockFlipped())
3212 return point;
3213
3214 // This is similar to RenderBox::flipForWritingModeForChild. We have to subtract out our left/top offsets twice, since
3215 // it's going to get added back in. We hide this complication here so that the calling code looks normal for the unflipped
3216 // case.
3217 if (isHorizontalWritingMode())
3218 return LayoutPoint(point.x(), point.y() + height() - child.renderer()->height() - 2 * child.locationOffsetOfBorderBox().height());
3219 return LayoutPoint(point.x() + width() - child.renderer()->width() - 2 * child.locationOffsetOfBorderBox().width(), point.y());
3220}
3221
3222LayoutUnit RenderBlockFlow::computedClearDeltaForChild(RenderBox& child, LayoutUnit logicalTop)
3223{
3224 // There is no need to compute clearance if we have no floats.
3225 if (!containsFloats())
3226 return 0;
3227
3228 // At least one float is present. We need to perform the clearance computation.
3229 UsedClear usedClear = RenderStyle::usedClear(child);
3230 bool clearSet = usedClear != UsedClear::None;
3231 LayoutUnit logicalBottom;
3232 switch (usedClear) {
3233 case UsedClear::None:
3234 break;
3235 case UsedClear::Left:
3236 logicalBottom = lowestFloatLogicalBottom(FloatingObject::FloatLeft);
3237 break;
3238 case UsedClear::Right:
3239 logicalBottom = lowestFloatLogicalBottom(FloatingObject::FloatRight);
3240 break;
3241 case UsedClear::Both:
3242 logicalBottom = lowestFloatLogicalBottom();
3243 break;
3244 }
3245
3246 // We also clear floats if we are too big to sit on the same line as a float (and wish to avoid floats by default).
3247 LayoutUnit result = clearSet ? std::max<LayoutUnit>(0, logicalBottom - logicalTop) : 0_lu;
3248 if (!result && child.avoidsFloats()) {
3249 LayoutUnit newLogicalTop = logicalTop;
3250 while (true) {
3251 LayoutUnit availableLogicalWidthAtNewLogicalTopOffset = availableLogicalWidthForLine(newLogicalTop, logicalHeightForChild(child));
3252 if (availableLogicalWidthAtNewLogicalTopOffset == availableLogicalWidthForContent())
3253 return newLogicalTop - logicalTop;
3254
3255 LayoutRect borderBox = child.borderBoxRect();
3256 LayoutUnit childLogicalWidthAtOldLogicalTopOffset = isHorizontalWritingMode() ? borderBox.width() : borderBox.height();
3257
3258 // FIXME: None of this is right for perpendicular writing-mode children.
3259 LayoutUnit childOldLogicalWidth = child.logicalWidth();
3260 LayoutUnit childOldMarginLeft = child.marginLeft();
3261 LayoutUnit childOldMarginRight = child.marginRight();
3262 LayoutUnit childOldLogicalTop = child.logicalTop();
3263
3264 child.setLogicalTop(newLogicalTop);
3265 child.updateLogicalWidth();
3266 borderBox = child.borderBoxRect();
3267 LayoutUnit childLogicalWidthAtNewLogicalTopOffset = isHorizontalWritingMode() ? borderBox.width() : borderBox.height();
3268
3269 child.setLogicalTop(childOldLogicalTop);
3270 child.setLogicalWidth(childOldLogicalWidth);
3271 child.setMarginLeft(childOldMarginLeft);
3272 child.setMarginRight(childOldMarginRight);
3273
3274 if (childLogicalWidthAtNewLogicalTopOffset <= availableLogicalWidthAtNewLogicalTopOffset) {
3275 // Even though we may not be moving, if the logical width did shrink because of the presence of new floats, then
3276 // we need to force a relayout as though we shifted. This happens because of the dynamic addition of overhanging floats
3277 // from previous siblings when negative margins exist on a child (see the addOverhangingFloats call at the end of collapseMargins).
3278 if (childLogicalWidthAtOldLogicalTopOffset != childLogicalWidthAtNewLogicalTopOffset)
3279 child.setChildNeedsLayout(MarkOnlyThis);
3280 return newLogicalTop - logicalTop;
3281 }
3282
3283 newLogicalTop = nextFloatLogicalBottomBelowForBlock(newLogicalTop);
3284 ASSERT(newLogicalTop >= logicalTop)((void)0);
3285 if (newLogicalTop < logicalTop)
3286 break;
3287 }
3288 ASSERT_NOT_REACHED()((void)0);
3289 }
3290 return result;
3291}
3292
3293bool RenderBlockFlow::hitTestFloats(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset)
3294{
3295 if (!m_floatingObjects)
3296 return false;
3297
3298 LayoutPoint adjustedLocation = accumulatedOffset;
3299 if (auto* renderView = dynamicDowncast<RenderView>(*this))
3300 adjustedLocation += toLayoutSize(renderView->frameView().scrollPosition());
3301
3302 for (auto& floatingObject : m_floatingObjects->set() | std::views::reverse) {
3303 auto* renderer = floatingObject->renderer();
3304 if (!renderer)
3305 continue;
3306 if (floatingObject->shouldPaint()) {
3307 LayoutPoint childPoint = flipFloatForWritingModeForChild(*floatingObject, adjustedLocation + floatingObject->translationOffsetToAncestor());
3308 if (renderer->hitTest(request, result, locationInContainer, childPoint)) {
3309 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(childPoint));
3310 return true;
3311 }
3312 }
3313 }
3314
3315 return false;
3316}
3317
3318bool RenderBlockFlow::hitTestInlineChildren(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
3319{
3320 ASSERT(childrenInline())((void)0);
3321
3322 return inlineLayout() && inlineLayout()->hitTest(request, result, locationInContainer, accumulatedOffset, hitTestAction);
3323}
3324
3325void RenderBlockFlow::addOverflowFromInlineChildren()
3326{
3327 if (inlineLayout()) {
3328 inlineLayout()->collectOverflow();
3329 return;
3330 }
3331
3332 if (svgTextLayout())
3333 svgTextLayout()->addOverflowFromInlineChildren();
3334}
3335
3336void RenderBlockFlow::addOverflowFromInFlowChildren(OptionSet<ComputeOverflowOptions> options)
3337{
3338 if (childrenInline()) {
3339 addOverflowFromInlineChildren();
3340
3341 // If this block is flowed inside a flow thread, make sure its overflow is propagated to the containing fragments.
3342 if (m_overflow) {
3343 if (CheckedPtr flow = enclosingFragmentedFlow())
3344 flow->addFragmentsVisualOverflow(*this, m_overflow->visualOverflowRect());
3345 }
3346 } else
3347 RenderBlock::addOverflowFromInFlowChildren(options);
3348}
3349
3350static float lineHeightForEmptyContent(auto& style, auto shouldNotRoundToIntegral)
3351{
3352 auto& fontMetrics = style.metricsOfPrimaryFont();
3353 auto ascent = shouldNotRoundToIntegral ? fontMetrics.ascent() : fontMetrics.intAscent();
3354 auto fontHeight = shouldNotRoundToIntegral ? fontMetrics.height() : fontMetrics.intHeight();
3355 return ascent + (style.computedLineHeight() - fontHeight) / 2.f;
3356}
3357
3358std::optional<LayoutUnit> RenderBlockFlow::firstLineBaseline() const
3359{
3360 if (isWritingModeRoot() && !isGridItem() && !isFlexItem())
3361 return { };
3362
3363 if (shouldApplyLayoutContainment())
3364 return { };
3365
3366 if (!childrenInline())
3367 return RenderBlock::firstLineBaseline();
3368
3369 if (auto* lineLayout = this->inlineLayout())
3370 return lineLayout->firstLineBaseline();
3371
3372 if (hasLineIfEmpty())
3373 return LayoutUnit { borderAndPaddingBefore() + lineHeightForEmptyContent(firstLineStyle(), settings().subpixelInlineLayoutEnabled()) };
3374
3375 return { };
3376}
3377
3378std::optional<LayoutUnit> RenderBlockFlow::lastLineBaseline() const
3379{
3380 if (isWritingModeRoot() && !isGridItem() && !isFlexItem())
3381 return { };
3382
3383 if (shouldApplyLayoutContainment())
3384 return { };
3385
3386 if (!childrenInline())
3387 return RenderBlock::lastLineBaseline();
3388
3389 if (auto* lineLayout = this->inlineLayout())
3390 return lineLayout->lastLineBaseline();
3391
3392 if (hasLineIfEmpty())
3393 return LayoutUnit { borderAndPaddingBefore() + lineHeightForEmptyContent(style(), settings().subpixelInlineLayoutEnabled()) };
3394
3395 return { };
3396}
3397
3398LayoutUnit RenderBlockFlow::adjustEnclosingTopForPrecedingBlock(LayoutUnit top) const
3399{
3400 if (selectionState() != RenderObject::HighlightState::Inside && selectionState() != RenderObject::HighlightState::End)
3401 return top;
3402
3403 if (isSelectionRoot())
3404 return top;
3405
3406 LayoutSize offsetToBlockBefore;
3407
3408 auto blockBeforeWithinSelectionRoot = [&]() -> const RenderBlockFlow* {
3409 const RenderElement* object = this;
3410 const RenderObject* sibling = nullptr;
3411 do {
3412 sibling = object->previousInFlowSibling();
3413 while (sibling) {
3414 auto* siblingBlock = dynamicDowncast<RenderBlock>(*sibling);
3415 if (siblingBlock && !siblingBlock->isSelectionRoot())
3416 break;
3417 sibling = sibling->previousInFlowSibling();
3418 }
3419
3420 auto& objectBlock = downcast<RenderBlock>(*object);
3421 offsetToBlockBefore -= LayoutSize(objectBlock.logicalLeft(), objectBlock.logicalTop());
3422 object = object->parent();
3423 } while (!sibling && is<RenderBlock>(object) && !downcast<RenderBlock>(*object).isSelectionRoot());
3424
3425 if (!sibling)
3426 return nullptr;
3427
3428 auto* beforeBlock = downcast<RenderBlock>(sibling);
3429
3430 offsetToBlockBefore += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop());
3431
3432 auto* child = beforeBlock->lastChild();
3433 while (auto* childBlock = dynamicDowncast<RenderBlock>(child)) {
3434 beforeBlock = childBlock;
3435 offsetToBlockBefore += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop());
3436 child = beforeBlock->lastChild();
3437 }
3438 return dynamicDowncast<RenderBlockFlow>(beforeBlock);
3439 };
3440
3441 auto* blockBefore = blockBeforeWithinSelectionRoot();
3442 if (!blockBefore)
3443 return top;
3444
3445 // Do not adjust blocks sharing the same line.
3446 if (!offsetToBlockBefore.height())
3447 return top;
3448
3449 if (auto lastLineBox = InlineIterator::lastLineBoxFor(*blockBefore)) {
3450 auto lastLineSelectionState = LineSelection::selectionState(*lastLineBox);
3451 if (lastLineSelectionState != RenderObject::HighlightState::Inside && lastLineSelectionState != RenderObject::HighlightState::Start)
3452 return top;
3453
3454 auto lastLineSelectionBottom = LineSelection::logicalBottom(*lastLineBox) + offsetToBlockBefore.height();
3455 top = std::max(top, LayoutUnit { lastLineSelectionBottom });
3456 }
3457 return top;
3458}
3459
3460GapRects RenderBlockFlow::inlineSelectionGaps(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
3461 LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo)
3462{
3463 ASSERT(!isSkippedContent())((void)0);
3464
3465 auto updateLastLogicalValues = [&](auto logicalTop, auto logicalLeft, auto logicalRight) {
3466 lastLogicalTop = logicalTop;
3467 lastLogicalLeft = logicalLeft;
3468 lastLogicalRight = logicalRight;
3469 };
3470
3471 bool containsStart = selectionState() == HighlightState::Start || selectionState() == HighlightState::Both;
3472 if (isSkippedContentRoot(*this)) {
3473 if (containsStart)
3474 updateLastLogicalValues(blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalHeight(), logicalLeftOffsetForContent(), logicalRightOffsetForContent());
3475 return { };
3476 }
3477
3478 // FIXME: Do we really need to check for SVG content here?
3479 auto hasInlineOrSVGContent = hasContentfulInlineLine() || (svgTextLayout() && svgTextLayout()->lineCount());
3480 if (!hasInlineOrSVGContent) {
3481 // Update our lastLogicalTop to be the bottom of the block. <hr>s or empty blocks with height can trip this case.
3482 if (containsStart)
3483 updateLastLogicalValues(blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalHeight(), logicalLeftSelectionOffset(rootBlock, logicalHeight(), cache), logicalRightSelectionOffset(rootBlock, logicalHeight(), cache));
3484 return { };
3485 }
3486
3487 auto hasSelectedChildren = [&](const InlineIterator::LineBoxIterator& lineBox) {
3488 return LineSelection::selectionState(*lineBox) != RenderObject::HighlightState::None;
3489 };
3490
3491 auto lineSelectionGap = [&](const InlineIterator::LineBoxIterator& lineBox, LayoutUnit selTop, LayoutUnit selHeight) -> GapRects {
3492 auto lineState = LineSelection::selectionState(*lineBox);
3493
3494 bool leftGap, rightGap;
3495 getSelectionGapInfo(lineState, leftGap, rightGap);
3496
3497 GapRects result;
3498
3499 auto firstSelectedBox = [&]() -> InlineIterator::LeafBoxIterator {
3500 for (auto box = lineBox->lineLeftmostLeafBox(); box; box.traverseLineRightwardOnLine()) {
3501 if (box->selectionState() != RenderObject::HighlightState::None)
3502 return box;
3503 }
3504 return { };
3505 }();
3506
3507 auto lastSelectedBox = [&]() -> InlineIterator::LeafBoxIterator {
3508 for (auto box = lineBox->lineRightmostLeafBox(); box; box.traverseLineLeftwardOnLine()) {
3509 if (box->selectionState() != RenderObject::HighlightState::None)
3510 return box;
3511 }
3512 return { };
3513 }();
3514
3515 if (leftGap) {
3516 result.uniteLeft(logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, firstSelectedBox->renderer().parent(), LayoutUnit(firstSelectedBox->logicalLeftIgnoringInlineDirection()),
3517 selTop, selHeight, cache, paintInfo));
3518 }
3519 if (rightGap) {
3520 result.uniteRight(logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastSelectedBox->renderer().parent(), LayoutUnit(lastSelectedBox->logicalRightIgnoringInlineDirection()),
3521 selTop, selHeight, cache, paintInfo));
3522 }
3523
3524 // When dealing with bidi text, a non-contiguous selection region is possible.
3525 // e.g. The logical text aaaAAAbbb (capitals denote RTL text and non-capitals LTR) is layed out
3526 // visually as 3 text runs |aaa|bbb|AAA| if we select 4 characters from the start of the text the
3527 // selection will look like (underline denotes selection):
3528 // |aaa|bbb|AAA|
3529 // ___ _
3530 // We can see that the |bbb| run is not part of the selection while the runs around it are.
3531 if (firstSelectedBox && firstSelectedBox != lastSelectedBox) {
3532 // Now fill in any gaps on the line that occurred between two selected elements.
3533 LayoutUnit lastLogicalLeft { firstSelectedBox->logicalRightIgnoringInlineDirection() };
3534 bool isPreviousBoxSelected = firstSelectedBox->selectionState() != RenderObject::HighlightState::None;
3535 for (auto box = firstSelectedBox; box; box.traverseLineRightwardOnLine()) {
3536 if (box->selectionState() != RenderObject::HighlightState::None) {
3537 LayoutRect logicalRect { lastLogicalLeft, selTop, LayoutUnit(box->logicalLeftIgnoringInlineDirection() - lastLogicalLeft), selHeight };
3538 logicalRect.move(isHorizontalWritingMode() ? offsetFromRootBlock : LayoutSize(offsetFromRootBlock.height(), offsetFromRootBlock.width()));
3539 LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, logicalRect);
3540 if (isPreviousBoxSelected && gapRect.width() > 0 && gapRect.height() > 0) {
3541 if (paintInfo && box->renderer().parent()->style().usedVisibility() == Visibility::Visible)
3542 paintInfo->context().fillRect(gapRect, box->renderer().parent()->selectionBackgroundColor());
3543 // VisibleSelection may be non-contiguous, see comment above.
3544 result.uniteCenter(gapRect);
3545 }
3546 lastLogicalLeft = box->logicalRightIgnoringInlineDirection();
3547 }
3548 if (box == lastSelectedBox)
3549 break;
3550 isPreviousBoxSelected = box->selectionState() != RenderObject::HighlightState::None;
3551 }
3552 }
3553
3554 return result;
3555 };
3556
3557 InlineIterator::LineBoxIterator lastSelectedLineBox;
3558 auto lineBox = InlineIterator::firstLineBoxFor(*this);
3559 for (; lineBox && !hasSelectedChildren(lineBox); lineBox.traverseNext()) { }
3560
3561 GapRects result;
3562
3563 // Now paint the gaps for the lines.
3564 for (; lineBox && hasSelectedChildren(lineBox); lineBox.traverseNext()) {
3565 auto selectionTop = LayoutUnit { LineSelection::logicalTopAdjustedForPrecedingBlock(*lineBox) };
3566 auto selectionHeight = LayoutUnit { std::max(0.f, LineSelection::logicalBottom(*lineBox) - selectionTop) };
3567
3568 if (!containsStart && !lastSelectedLineBox
3569 && selectionState() != HighlightState::Start
3570 && selectionState() != HighlightState::Both)
3571 result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, selectionTop, cache, paintInfo));
3572
3573 LayoutRect logicalRect { LayoutUnit(lineBox->contentLogicalLeft()), selectionTop, LayoutUnit(lineBox->contentLogicalWidth()), selectionTop + selectionHeight };
3574 logicalRect.move(isHorizontalWritingMode() ? offsetFromRootBlock : offsetFromRootBlock.transposedSize());
3575 LayoutRect physicalRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, logicalRect);
3576 if (!paintInfo || (isHorizontalWritingMode() && physicalRect.y() < paintInfo->rect.maxY() && physicalRect.maxY() > paintInfo->rect.y())
3577 || (!isHorizontalWritingMode() && physicalRect.x() < paintInfo->rect.maxX() && physicalRect.maxX() > paintInfo->rect.x()))
3578 result.unite(lineSelectionGap(lineBox, selectionTop, selectionHeight));
3579
3580 lastSelectedLineBox = lineBox;
3581 }
3582
3583 if (containsStart && !lastSelectedLineBox) {
3584 // VisibleSelection must start just after our last line.
3585 lastSelectedLineBox = InlineIterator::lastLineBoxFor(*this);
3586 }
3587
3588 if (lastSelectedLineBox && selectionState() != HighlightState::End && selectionState() != HighlightState::Both) {
3589 // Update our lastY to be the bottom of the last selected line.
3590 auto lastLineSelectionBottom = LayoutUnit { LineSelection::logicalBottom(*lastSelectedLineBox) };
3591 updateLastLogicalValues(blockDirectionOffset(rootBlock, offsetFromRootBlock) + lastLineSelectionBottom, logicalLeftSelectionOffset(rootBlock, lastLineSelectionBottom, cache), logicalRightSelectionOffset(rootBlock, lastLineSelectionBottom, cache));
3592 }
3593 return result;
3594}
3595
3596bool RenderBlockFlow::needsLayoutAfterFragmentRangeChange() const
3597{
3598 // A block without floats or that expands to enclose them won't need a relayout
3599 // after a fragment range change. There is no overflow content needing relayout
3600 // in the fragment chain because the fragment range can only shrink after the estimation.
3601 if (!containsFloats() || createsNewFormattingContext())
3602 return false;
3603
3604 return true;
3605}
3606
3607void RenderBlockFlow::setMultiColumnFlow(RenderMultiColumnFlow& fragmentedFlow)
3608{
3609 ASSERT(!hasRareBlockFlowData() || !rareBlockFlowData()->m_multiColumnFlow)((void)0);
3610 ensureRareBlockFlowData().m_multiColumnFlow = fragmentedFlow;
3611}
3612
3613void RenderBlockFlow::clearMultiColumnFlow()
3614{
3615 ASSERT(hasRareBlockFlowData())((void)0);
3616 ASSERT(rareBlockFlowData()->m_multiColumnFlow)((void)0);
3617 rareBlockFlowData()->m_multiColumnFlow = { };
3618}
3619
3620int RenderBlockFlow::lineCount() const
3621{
3622 if (!childrenInline()) {
3623 ASSERT_NOT_REACHED()((void)0);
3624 return 0;
3625 }
3626 if (inlineLayout())
3627 return inlineLayout()->lineCount();
3628 if (svgTextLayout())
3629 return svgTextLayout()->lineCount();
3630
3631 return 0;
3632}
3633
3634bool RenderBlockFlow::containsNonZeroBidiLevel() const
3635{
3636 for (auto lineBox = InlineIterator::firstLineBoxFor(*this); lineBox; lineBox.traverseNext()) {
3637 for (auto box = lineBox->lineLeftmostLeafBox(); box; box = box.traverseLineRightwardOnLine()) {
3638 if (box->bidiLevel())
3639 return true;
3640 }
3641 }
3642 return false;
3643}
3644
3645static Position positionForRun(const RenderBlockFlow& flow, InlineIterator::BoxIterator box, bool start)
3646{
3647 if (!box)
3648 return Position();
3649
3650 if (!box->renderer().nonPseudoNode())
3651 return makeDeprecatedLegacyPosition(flow.nonPseudoElement(), start ? flow.caretMinOffset() : flow.caretMaxOffset());
3652
3653 auto* textBox = dynamicDowncast<InlineIterator::TextBoxIterator>(box);
3654 if (!textBox)
3655 return makeDeprecatedLegacyPosition(box->renderer().nonPseudoNode(), start ? box->renderer().caretMinOffset() : box->renderer().caretMaxOffset());
3656
3657 return makeDeprecatedLegacyPosition((*textBox)->renderer().nonPseudoNode(), start ? (*textBox)->start() : (*textBox)->end());
3658}
3659
3660RenderText* RenderBlockFlow::findClosestTextAtAbsolutePoint(const FloatPoint& point)
3661{
3662 // A light, non-recursive version of RenderBlock::positionForCoordinates that looks at
3663 // whether a point lies within the gaps between its root line boxes, to be called against
3664 // a node returned from elementAtPoint. We make the assumption that either the node or one
3665 // of its immediate children contains the root line boxes in question.
3666 // See <rdar://problem/6824650> for context.
3667
3668 RenderBlock* block = this;
3669
3670 FloatPoint localPoint = block->absoluteToLocal(point);
3671
3672 if (!block->childrenInline()) {
3673 // Look among our immediate children for an alternate box that contains the point.
3674 for (RenderBox* child = block->firstChildBox(); child; child = child->nextSiblingBox()) {
3675 if (!child->height() || child->style().usedVisibility() != WebCore::Visibility::Visible || child->isFloatingOrOutOfFlowPositioned())
3676 continue;
3677 float top = child->y();
3678
3679 RenderBox* nextChild = child->nextSiblingBox();
3680 while (nextChild && nextChild->isFloatingOrOutOfFlowPositioned())
3681 nextChild = nextChild->nextSiblingBox();
3682 if (!nextChild) {
3683 if (localPoint.y() >= top) {
3684 block = downcast<RenderBlock>(child);
3685 break;
3686 }
3687 continue;
3688 }
3689
3690 float bottom = nextChild->y();
3691
3692 if (localPoint.y() >= top && localPoint.y() < bottom) {
3693 if (auto* childAsBlock = dynamicDowncast<RenderBlock>(*child)) {
3694 block = childAsBlock;
3695 break;
3696 }
3697 }
3698 }
3699
3700 if (!block->childrenInline())
3701 return nullptr;
3702
3703 localPoint = block->absoluteToLocal(point);
3704 }
3705
3706 RenderBlockFlow& blockFlow = downcast<RenderBlockFlow>(*block);
3707
3708 // Only check the gaps between the root line boxes. We deliberately ignore overflow because
3709 // experience has shown that hit tests on an exploded text node can fail when within the
3710 // overflow fragment.
3711 auto previousRootInlineBoxBottom = std::optional<float> { };
3712 for (auto box = InlineIterator::firstRootInlineBoxFor(blockFlow); box; box.traverseInlineBoxLineRightward()) {
3713 if (previousRootInlineBoxBottom) {
3714 if (localPoint.y() < *previousRootInlineBoxBottom)
3715 return nullptr;
3716
3717 if (localPoint.y() > *previousRootInlineBoxBottom && localPoint.y() < box->logicalTop()) {
3718 if (auto closestBox = closestBoxForHorizontalPosition(*box->lineBox(), localPoint.x())) {
3719 if (auto* textRenderer = dynamicDowncast<RenderText>(closestBox->renderer()))
3720 return const_cast<RenderText*>(textRenderer);
3721 }
3722 }
3723 }
3724 previousRootInlineBoxBottom = box->logicalBottom();
3725 }
3726 return nullptr;
3727}
3728
3729PositionWithAffinity RenderBlockFlow::positionForPointWithInlineChildren(const LayoutPoint& pointInLogicalContents, HitTestSource source)
3730{
3731 ASSERT(childrenInline())((void)0);
3732
3733 auto firstLineBox = InlineIterator::firstLineBoxFor(*this);
3734
3735 if (!firstLineBox)
3736 return createPositionWithAffinity(0, Affinity::Downstream);
3737
3738 bool linesAreFlipped = writingMode().isLineInverted();
3739 bool blocksAreFlipped = writingMode().isBlockFlipped();
3740
3741 // look for the closest line box in the root box which is at the passed-in y coordinate
3742 InlineIterator::LeafBoxIterator closestBox;
3743 InlineIterator::LineBoxIterator firstLineBoxWithChildren;
3744 InlineIterator::LineBoxIterator lastLineBoxWithChildren;
3745 for (auto lineBox = firstLineBox; lineBox; lineBox.traverseNext()) {
3746 if (!lineBox->lineLeftmostLeafBox())
3747 continue;
3748 if (!firstLineBoxWithChildren)
3749 firstLineBoxWithChildren = lineBox;
3750
3751 if (!linesAreFlipped && lineBox->isFirstAfterPageBreak()
3752 && (pointInLogicalContents.y() < lineBox->logicalTop() || (blocksAreFlipped && pointInLogicalContents.y() == lineBox->logicalTop())))
3753 break;
3754
3755 lastLineBoxWithChildren = lineBox;
3756
3757 // check if this root line box is located at this y coordinate
3758 auto selectionBottom = LineSelection::logicalBottom(*lineBox);
3759 if (pointInLogicalContents.y() < selectionBottom || (blocksAreFlipped && pointInLogicalContents.y() == selectionBottom)) {
3760 if (linesAreFlipped) {
3761 auto nextLineBoxWithChildren = lineBox->next();
3762 while (nextLineBoxWithChildren && !nextLineBoxWithChildren->lineLeftmostLeafBox())
3763 nextLineBoxWithChildren.traverseNext();
3764
3765 if (nextLineBoxWithChildren && nextLineBoxWithChildren->isFirstAfterPageBreak()
3766 && (pointInLogicalContents.y() > nextLineBoxWithChildren->logicalTop() || (!blocksAreFlipped && pointInLogicalContents.y() == nextLineBoxWithChildren->logicalTop())))
3767 continue;
3768 }
3769 closestBox = closestBoxForHorizontalPosition(*lineBox, pointInLogicalContents.x());
3770 if (closestBox)
3771 break;
3772 }
3773 }
3774
3775 bool moveCaretToBoundary = protect(protect(frame())->editor())->behavior().shouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom();
3776
3777 if (!moveCaretToBoundary && !closestBox && lastLineBoxWithChildren) {
3778 // y coordinate is below last root line box, pretend we hit it
3779 closestBox = closestBoxForHorizontalPosition(*lastLineBoxWithChildren, pointInLogicalContents.x());
3780 }
3781
3782 if (closestBox) {
3783 if (moveCaretToBoundary) {
3784 auto firstLineWithChildrenTop = LayoutUnit { std::min(previousLineBoxContentBottomOrBorderAndPadding(*firstLineBoxWithChildren), firstLineBoxWithChildren->contentLogicalTop()) };
3785 if (pointInLogicalContents.y() < firstLineWithChildrenTop
3786 || (blocksAreFlipped && pointInLogicalContents.y() == firstLineWithChildrenTop)) {
3787 auto box = firstLineBoxWithChildren->lineLeftmostLeafBox();
3788 if (box->isLineBreak()) {
3789 if (auto next = box->nextLineRightwardOnLineIgnoringLineBreak())
3790 box = next;
3791 }
3792 // y coordinate is above first root line box, so return the start of the first
3793 return positionForRun(*this, box, true);
3794 }
3795 }
3796
3797 // pass the box a top position that is inside it
3798 auto point = LayoutPoint { pointInLogicalContents.x(), contentStartInBlockDirection(*closestBox->lineBox()) };
3799 if (!isHorizontalWritingMode())
3800 point = point.transposedPoint();
3801 if (closestBox->renderer().isBlockLevelReplacedOrAtomicInline())
3802 return positionForPointRespectingEditingBoundaries(*this, const_cast<RenderBox&>(downcast<RenderBox>(closestBox->renderer())), point, source);
3803 return const_cast<RenderObject&>(closestBox->renderer()).positionForPoint(point, source, nullptr);
3804 }
3805
3806 if (lastLineBoxWithChildren) {
3807 // We hit this case for Mac behavior when the Y coordinate is below the last box.
3808 if (auto blockBox = lastLineBoxWithChildren->blockLevelBox()) {
3809 auto& childBlockRenderer = const_cast<RenderBox&>(downcast<RenderBox>(blockBox->renderer()));
3810 return positionForPointRespectingEditingBoundaries(*this, childBlockRenderer, pointInLogicalContents, source);
3811 }
3812 ASSERT(moveCaretToBoundary)((void)0);
3813 InlineIterator::LineLogicalOrderCache orderCache;
3814 if (auto logicallyLastBox = InlineIterator::lastLeafOnLineInLogicalOrderWithNode(lastLineBoxWithChildren, orderCache))
3815 return positionForRun(*this, logicallyLastBox, false);
3816 }
3817
3818 // Can't reach this. We have a root line box, but it has no kids.
3819 // FIXME: This should ASSERT_NOT_REACHED(), but clicking on placeholder text
3820 // seems to hit this code path.
3821 return createPositionWithAffinity(0, Affinity::Downstream);
3822}
3823
3824Position RenderBlockFlow::positionForPoint(const LayoutPoint& point, HitTestSource source)
3825{
3826 return positionForPoint(point, source, nullptr).position();
3827}
3828
3829PositionWithAffinity RenderBlockFlow::positionForPoint(const LayoutPoint& point, HitTestSource source, const RenderFragmentContainer*)
3830{
3831 return RenderBlock::positionForPoint(point, source, nullptr);
3832}
3833
3834void RenderBlockFlow::paintInlineChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
3835{
3836 ASSERT(childrenInline())((void)0);
3837
3838 if (!inlineLayout())
3839 return;
3840
3841 inlineLayout()->paint(paintInfo, paintOffset);
3842}
3843
3844void RenderBlockFlow::paintBlockLevelContentInInline(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
3845{
3846 ASSERT(childrenInline())((void)0);
3847
3848 PaintInfo paintInfoForChildren = paintInfoForBlockChildren(paintInfo);
3849
3850 for (auto walker = InlineWalker(*this); !walker.atEnd(); walker.advance()) {
3851 auto* child = dynamicDowncast<RenderBox>(walker.current());
3852 if (!child)
3853 continue;
3854 if (!paintChild(*child, paintInfo, paintOffset, paintInfoForChildren, false))
3855 return;
3856 }
3857}
3858
3859bool RenderBlockFlow::relayoutForPagination()
3860{
3861 if (!multiColumnFlow() || !multiColumnFlow()->shouldRelayoutForPagination())
3862 return false;
3863
3864 multiColumnFlow()->setNeedsHeightsRecalculation(false);
3865 multiColumnFlow()->setInBalancingPass(true); // Prevent re-entering this method (and recursion into layout).
3866
3867 bool needsRelayout;
3868 bool neededRelayout = false;
3869 bool firstPass = true;
3870 do {
3871 // Column heights may change here because of balancing. We may have to do multiple layout
3872 // passes, depending on how the contents is fitted to the changed column heights. In most
3873 // cases, laying out again twice or even just once will suffice. Sometimes we need more
3874 // passes than that, though, but the number of retries should not exceed the number of
3875 // columns, unless we have a bug.
3876 needsRelayout = false;
3877 for (RenderMultiColumnSet* multicolSet = multiColumnFlow()->firstMultiColumnSet(); multicolSet; multicolSet = multicolSet->nextSiblingMultiColumnSet()) {
3878 if (multicolSet->recalculateColumnHeight(firstPass))
3879 needsRelayout = true;
3880 if (needsRelayout) {
3881 // Once a column set gets a new column height, that column set and all successive column
3882 // sets need to be laid out over again, since their logical top will be affected by
3883 // this, and therefore their column heights may change as well, at least if the multicol
3884 // height is constrained.
3885 multicolSet->setChildNeedsLayout(MarkOnlyThis);
3886 }
3887 }
3888 if (needsRelayout) {
3889 // Layout again. Column balancing resulted in a new height.
3890 neededRelayout = true;
3891 multiColumnFlow()->setChildNeedsLayout(MarkOnlyThis);
3892 setChildNeedsLayout(MarkOnlyThis);
3893 layoutBlock(RelayoutChildren::No);
3894 }
3895 firstPass = false;
3896 } while (needsRelayout);
3897
3898 multiColumnFlow()->setInBalancingPass(false);
3899
3900 return neededRelayout;
3901}
3902
3903bool RenderBlockFlow::hasContentfulInlineOrBlockLine() const
3904{
3905 return inlineLayout() ? inlineLayout()->hasContentfulInlineOrBlockLine() : false;
3906}
3907
3908bool RenderBlockFlow::hasContentfulInlineLine() const
3909{
3910 return inlineLayout() ? inlineLayout()->hasContentfulInlineLine() : false;
3911}
3912
3913bool RenderBlockFlow::hasBlocksInInlineLayout() const
3914{
3915 auto* inlineLayout = this->inlineLayout();
3916 return inlineLayout && inlineLayout->hasBlocks();
3917}
3918
3919void RenderBlockFlow::invalidateLineLayout(InvalidationReason invalidationReason)
3920{
3921 if (lineLayoutPath() == UndeterminedPath)
3922 return;
3923
3924 auto issueNeedsLayoutIfApplicable = [&] {
3925 if (selfNeedsLayout() || normalChildNeedsLayout())
3926 return;
3927 // FIXME: We should just kick off a subtree layout here (if needed at all) see webkit.org/b/172947.
3928 setNeedsLayout();
3929 };
3930
3931 if (inlineLayout()) {
3932 ASSERT(!m_previousInlineLayoutContentTopAndBottomIncludingInkOverflow)((void)0);
3933 m_previousInlineLayoutContentTopAndBottomIncludingInkOverflow = inlineContentTopAndBottomIncludingInkOverflow();
3934 }
3935
3936 switch (invalidationReason) {
3937 case InvalidationReason::InternalMove:
3938 if (AXObjectCache* cache = protect(document())->existingAXObjectCache())
3939 cache->deferRecomputeIsIgnored(protect(element()).get());
3940 break;
3941 case InvalidationReason::ContentChange: {
3942 // Since we eagerly remove the display content here, repaints issued between this invalidation (triggered by style change/content mutation) and the subsequent layout would produce empty rects.
3943 repaint();
3944 for (auto walker = InlineWalker(*this); !walker.atEnd(); walker.advance()) {
3945 auto& renderer = *walker.current();
3946 if (!renderer.everHadLayout())
3947 continue;
3948 if (!renderer.isInFlow() && inlineLayout()->contains(downcast<RenderElement>(renderer)))
3949 renderer.repaint();
3950 renderer.setNeedsPreferredWidthsUpdate();
3951 }
3952 issueNeedsLayoutIfApplicable();
3953 break;
3954 }
3955 case InvalidationReason::InsertionOrRemoval:
3956 setLineLayoutPath(UndeterminedPath);
3957 issueNeedsLayoutIfApplicable();
3958 break;
3959 default:
3960 break;
3961 }
3962
3963 m_lineLayout = std::monostate();
3964}
3965
3966bool RenderBlockFlow::layoutSimpleBlockContentInInline(MarginInfo& marginInfo)
3967{
3968 if (!inlineLayout())
3969 return false;
3970
3971 for (auto walker = InlineWalker(*this); !walker.atEnd(); walker.advance()) {
3972 ASSERT(!walker.current()->selfNeedsLayout())((void)0);
3973
3974 auto* blockRenderer = dynamicDowncast<RenderBox>(*walker.current());
3975 if (!blockRenderer || !blockRenderer->isBlockLevelBox())
3976 continue;
3977
3978 auto logicalHeight = blockRenderer->logicalHeight();
3979 auto isEligibleForBlockOnlyLayout = [&] {
3980 // Do not interfere with margin collapsing.
3981 if (!blockRenderer->isInFlow() || !logicalHeight)
3982 return false;
3983 // FIXME: This should be some narrower test.
3984 if (blockRenderer->isRenderTable())
3985 return false;
3986 if (CheckedPtr renderBlock = dynamicDowncast<RenderBlock>(*blockRenderer))
3987 return !renderBlock->containsFloats();
3988 return true;
3989 };
3990 if (!isEligibleForBlockOnlyLayout())
3991 return false;
3992
3993 auto displayBox = InlineIterator::boxFor(*blockRenderer);
3994 if (!displayBox) {
3995 ASSERT_NOT_REACHED()((void)0);
3996 return false;
3997 }
3998
3999 auto borderBoxLogicalTop = blockRenderer->logicalTop();
4000 auto marginBoxLogicalTop = borderBoxLogicalTop;
4001
4002 if (!marginInfo.canCollapseWithMarginBefore()) {
4003 // Although this box is not expected to change position or size (since no self-layout is set),
4004 // we treat layout as starting at the box's top margin to avoid confusion when the container performs layout on it.
4005 // This logic is copied from estimateLogicalTopPosition.
4006 auto marginValues = marginValuesForChild(*blockRenderer);
4007 marginBoxLogicalTop -= std::max(marginInfo.positiveMargin(), marginValues.positiveMarginBefore()) - std::max(marginInfo.negativeMargin(), marginValues.negativeMarginBefore());
4008 }
4009
4010 marginInfo = layoutBlockChildFromInlineLayout(*blockRenderer, marginBoxLogicalTop, marginInfo).marginInfo;
4011 auto shouldFallbackToNormalInlineLayout = [&] {
4012 if (logicalHeight != blockRenderer->logicalHeight())
4013 return true;
4014 if (CheckedPtr renderBlock = dynamicDowncast<RenderBlock>(*blockRenderer))
4015 return renderBlock->containsFloats();
4016 return false;
4017 };
4018 if (shouldFallbackToNormalInlineLayout())
4019 return false;
4020 blockRenderer->setLogicalTop(borderBoxLogicalTop);
4021 }
4022 inlineLayout()->updateOverflow();
4023 return true;
4024}
4025
4026static bool NODELETE[[clang::annotate_type("webkit.nodelete")]] hasSimpleStaticPositionForInlineLevelOutOfFlowChildrenByStyle(const RenderStyle& rootStyle)
4027{
4028 if (rootStyle.textAlign() != Style::TextAlign::Start)
4029 return false;
4030 if (!rootStyle.textIndent().length.isKnownZero())
4031 return false;
4032 return true;
4033}
4034
4035static void setFullRepaintOnParentInlineBoxLayerIfNeeded(const RenderText& renderer)
4036{
4037 // Repaints (on self) are normally issued either during layout using LayoutRepainter inside ::layout() functions (#1)
4038 // or after layout, while recursing the layer tree (#2).
4039 // Additionally, repaint at the block level (#3) takes care of regular in-flow content.
4040 // However in case of text content, we don't have (#1), (#2) is primarily a geometry diff type of repaint meaning
4041 // no repaint happens unless content size changes (or full repaint bit is set on the layer)
4042 // and (#3) only works when the block container and the text content share the same layer.
4043 // Here we mark the parent inline box's layer dirty to trigger repaint at (#2).
4044 if (!renderer.needsLayout())
4045 return;
4046 CheckedPtr parent = renderer.parent();
4047 if (!parent) {
4048 ASSERT_NOT_REACHED()((void)0);
4049 return;
4050 }
4051 if (!parent->isInline() || !parent->hasLayer())
4052 return;
4053 downcast<RenderLayerModelObject>(*parent).layer()->setRepaintStatus(RepaintStatus::NeedsFullRepaint);
4054}
4055
4056std::pair<float, float> RenderBlockFlow::inlineContentTopAndBottomIncludingInkOverflow() const
4057{
4058 if (m_previousInlineLayoutContentTopAndBottomIncludingInkOverflow)
4059 return *m_previousInlineLayoutContentTopAndBottomIncludingInkOverflow;
4060
4061 if (!inlineLayout())
4062 return { };
4063
4064 auto firstLineBox = InlineIterator::firstLineBoxFor(*this);
4065 auto lastLineBox = InlineIterator::lastLineBoxFor(*this);
4066 if (!firstLineBox)
4067 return { };
4068
4069 auto logicalTop = std::min(firstLineBox->logicalTop(), firstLineBox->contentLogicalTop());
4070 auto logicalBottom = std::max(lastLineBox->logicalBottom(), lastLineBox->contentLogicalBottom());
4071
4072 if (!inlineLayout()->hasInkOverflow())
4073 return { logicalTop, logicalBottom };
4074
4075 for (auto lineBox = firstLineBox; lineBox; lineBox.traverseNext()) {
4076 logicalTop = std::min(logicalTop, lineBox->inkOverflowLogicalTop());
4077 logicalBottom = std::max(logicalBottom, lineBox->inkOverflowLogicalBottom());
4078 }
4079 return { logicalTop, logicalBottom };
4080}
4081
4082RenderBlockFlow::InlineContentStatus RenderBlockFlow::markInlineContentDirtyForLayout(RelayoutChildren relayoutChildren)
4083{
4084 auto contentNeedsNormalChildLayoutOnly = std::optional<bool> { };
4085 auto hasInFlowBlockLevelElement = false;
4086 auto hasDirtyInFlowBlockLevelElement = false;
4087 auto hasSimpleOutOfFlowContentOnly = !hasLineIfEmpty();
4088 auto hasSimpleStaticPositionForInlineLevelOutOfFlowContentByStyle = hasSimpleStaticPositionForInlineLevelOutOfFlowChildrenByStyle(style());
4089
4090 for (auto walker = InlineWalker(*this); !walker.atEnd(); walker.advance()) {
4091 auto& renderer = *walker.current();
4092 auto* box = dynamicDowncast<RenderBox>(renderer);
4093 auto isInFlowBlockLevelElement = box && box->isBlockLevelBox() && box->isInFlow();
4094 hasInFlowBlockLevelElement |= isInFlowBlockLevelElement;
4095 hasDirtyInFlowBlockLevelElement |= (isInFlowBlockLevelElement && box->needsLayout());
4096 auto childNeedsLayout = relayoutChildren == RelayoutChildren::Yes || (box && box->hasRelativeDimensions() && !box->isBlockLevelBox());
4097 auto childNeedsPreferredWidthComputation = relayoutChildren == RelayoutChildren::Yes && box && box->shouldInvalidatePreferredWidths();
4098 if (childNeedsLayout)
4099 renderer.setNeedsLayout(MarkOnlyThis);
4100 if (childNeedsPreferredWidthComputation)
4101 renderer.setNeedsPreferredWidthsUpdate(MarkOnlyThis);
4102
4103 if (renderer.isOutOfFlowPositioned()) {
4104 renderer.containingBlock()->addOutOfFlowBox(*box);
4105 // FIXME: This is only needed because of the synchronous layout call in setStaticPositionsForSimpleOutOfFlowContent
4106 // which itself appears to be a workaround for a bad subtree layout shown by
4107 // fast/block/positioning/static_out_of_flow_inside_layout_boundary.html
4108 auto& style = downcast<RenderElement>(renderer).style();
4109 auto hasParentRelativeHeightOrTop = [&] {
4110 if (style.logicalHeight().isPercentOrCalculated() || style.logicalTop().isPercentOrCalculated())
4111 return true;
4112 return !style.logicalBottom().isAuto();
4113 }();
4114 if (hasParentRelativeHeightOrTop)
4115 hasSimpleOutOfFlowContentOnly = false;
4116
4117 if (hasSimpleOutOfFlowContentOnly && style.originalDisplay().isInlineType())
4118 hasSimpleOutOfFlowContentOnly = hasSimpleStaticPositionForInlineLevelOutOfFlowContentByStyle;
4119 } else
4120 hasSimpleOutOfFlowContentOnly = false;
4121
4122 if (!renderer.needsLayout() && !renderer.needsPreferredLogicalWidthsUpdate())
4123 continue;
4124
4125 if (auto* renderText = dynamicDowncast<RenderText>(renderer))
4126 setFullRepaintOnParentInlineBoxLayerIfNeeded(*renderText);
4127
4128 if (auto* inlineLevelBox = dynamicDowncast<RenderBox>(renderer)) {
4129 // FIXME: Move this to where the actual content change happens and call it on the parent IFC.
4130 auto shouldTriggerFullLayout = inlineLevelBox->isInline() && (inlineLevelBox->needsSimplifiedNormalFlowLayout() || inlineLevelBox->normalChildNeedsLayout() || inlineLevelBox->outOfFlowChildNeedsLayout()) && inlineLayout();
4131 if (shouldTriggerFullLayout) {
4132 inlineLayout()->boxContentWillChange(*inlineLevelBox);
4133 contentNeedsNormalChildLayoutOnly = false;
4134 }
4135 }
4136
4137 // Non inline box inline level elements (e.g. <img>) report self-needs-layout on style change, and RenderText also reports self-needs-layout on content change.
4138 // Inline boxes report normal-child-needs-layout when their children need (any) layout.
4139 contentNeedsNormalChildLayoutOnly = contentNeedsNormalChildLayoutOnly.value_or(true) && (!renderer.needsLayout() || renderer.needsNormalChildOrSimplifiedLayoutOnly());
4140
4141 if (isAnyOf<RenderLineBreak, RenderInline, RenderText>(renderer))
4142 renderer.clearNeedsLayout();
4143
4144#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)(defined ENABLE_ACCESSIBILITY_ISOLATED_TREE && ENABLE_ACCESSIBILITY_ISOLATED_TREE
)
4145 if (CheckedPtr cache = protect(document())->existingAXObjectCache())
4146 cache->onTextRunsChanged(renderer);
4147#endif
4148
4149 if (CheckedPtr renderCombineText = dynamicDowncast<RenderCombineText>(renderer)) {
4150 renderCombineText->combineTextIfNeeded();
4151 continue;
4152 }
4153 }
4154 return { hasSimpleOutOfFlowContentOnly, hasDirtyInFlowBlockLevelElement, hasInFlowBlockLevelElement ? contentNeedsNormalChildLayoutOnly : std::nullopt };
4155}
4156
4157std::optional<LayoutUnit> RenderBlockFlow::updateLineClampStateAndLogicalHeightAfterLayout()
4158{
4159 auto& layoutState = *view().frameView().layoutContext().layoutState();
4160 auto& inlineLayout = *this->inlineLayout();
4161
4162 auto legacyLineClamp = layoutState.legacyLineClamp();
4163 if (!legacyLineClamp || isFloatingOrOutOfFlowPositioned())
4164 return { };
4165
4166 legacyLineClamp->currentLineCount += inlineLayout.lineCount();
4167 if (legacyLineClamp->clampedRenderer) {
4168 // We've already clamped this flex container at a previous flex item.
4169 layoutState.setLegacyLineClamp(*legacyLineClamp);
4170 return { };
4171 }
4172
4173 auto clampedContentHeight = [&]() -> std::optional<LayoutUnit> {
4174 if (auto clampedHeight = inlineLayout.clampedContentLogicalHeight())
4175 return clampedHeight;
4176 if (legacyLineClamp->currentLineCount == legacyLineClamp->maximumLineCount) {
4177 // Even if we did not truncate the content, this might be our clamping position.
4178 return LayoutUnit { inlineLayout.contentLogicalHeight() };
4179 }
4180 return { };
4181 };
4182 if (auto logicalHeight = clampedContentHeight()) {
4183 legacyLineClamp->clampedContentLogicalHeight = logicalHeight;
4184 legacyLineClamp->clampedRenderer = this;
4185 layoutState.setLegacyLineClamp(*legacyLineClamp);
4186 return logicalHeight;
4187 }
4188 layoutState.setLegacyLineClamp(*legacyLineClamp);
4189 return { };
4190}
4191
4192void RenderBlockFlow::updateRepaintTopAndBottomAfterLayout(RelayoutChildren relayoutChildren, std::optional<LayoutRect> partialRepaintRect, std::pair<float, float> oldContentTopAndBottomIncludingInkOverflow, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom)
4193{
4194 auto isFullLayout = selfNeedsLayout() || relayoutChildren == RelayoutChildren::Yes;
4195 if (isFullLayout) {
4196 if (!selfNeedsLayout()) {
4197 // In order to really trigger full repaint, the block container has to have the self layout flag set (see LegacyLineLayout::layoutRunsAndFloats).
4198 // Without having it set, repaint after layout logic (see RenderElement::repaintAfterLayoutIfNeeded) only issues repaint on the diff of
4199 // before/after repaint bounds. It results in incorrect repaint when the inline content changes (new text) and expands the same time.
4200 // (it only affects shrink-to-fit type of containers).
4201 // FIXME: We have the exact damaged rect here, should be able to issue repaint on both inline and block directions.
4202 setNeedsLayout(MarkOnlyThis);
4203 }
4204 // Let's trigger full repaint instead for now (matching legacy line layout).
4205 // FIXME: We should revisit this behavior and run repaints strictly on visual overflow.
4206 repaintLogicalTop = { };
4207 repaintLogicalBottom = { };
4208 return;
4209 }
4210
4211 if (partialRepaintRect) {
4212 repaintLogicalTop = partialRepaintRect->y();
4213 repaintLogicalBottom = partialRepaintRect->maxY();
4214 return;
4215 }
4216
4217 auto contentTopAndBottomIncludingInkOverflow = inlineContentTopAndBottomIncludingInkOverflow();
4218 auto damageTopIncludingInkOverflow = std::min(oldContentTopAndBottomIncludingInkOverflow.first, contentTopAndBottomIncludingInkOverflow.first);
4219 auto damageBottomIncludingInkOverflow = std::max(oldContentTopAndBottomIncludingInkOverflow.second, contentTopAndBottomIncludingInkOverflow.second);
4220
4221 repaintLogicalTop = std::min(LayoutUnit::fromFloatFloor(damageTopIncludingInkOverflow), borderAndPaddingBefore());
4222 repaintLogicalBottom = std::max(LayoutUnit::fromFloatCeil(damageBottomIncludingInkOverflow), logicalHeight());
4223}
4224
4225void RenderBlockFlow::layoutInlineContent(RelayoutChildren relayoutChildren, LayoutUnit previousHeight, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom)
4226{
4227 auto inlineContentStatus = markInlineContentDirtyForLayout(relayoutChildren);
4228 if (inlineContentStatus.hasSimpleOutOfFlowContentOnly) {
4229 // Shortcut the layout.
4230 m_lineLayout = std::monostate();
4231
4232 setStaticPositionsForSimpleOutOfFlowContent();
4233 setLogicalHeight(borderAndPaddingLogicalHeight() + scrollbarLogicalHeight());
4234 return;
4235 }
4236
4237 auto oldContentTopAndBottomIncludingInkOverflow = inlineContentTopAndBottomIncludingInkOverflow();
4238 m_previousInlineLayoutContentTopAndBottomIncludingInkOverflow = { };
4239
4240 auto mayRunSimpleBlockContentInInlineLayout = inlineContentStatus.onlyBlockContentNeedsLayout && *inlineContentStatus.onlyBlockContentNeedsLayout;
4241 if (mayRunSimpleBlockContentInInlineLayout) {
4242 auto marginInfo = MarginInfo { *this, MarginInfo::IgnoreScrollbarForAfterMargin::No };
4243 if (layoutSimpleBlockContentInInline(marginInfo)) {
4244 setLogicalHeight(previousHeight);
4245 handleAfterSideOfBlock(marginInfo, previousHeight - (borderAndPaddingLogicalHeight() + scrollbarLogicalHeight()));
4246 // Pass empty rect as partialRepaintRect because child blocks issue their own repaints if needed.
4247 updateRepaintTopAndBottomAfterLayout(relayoutChildren, LayoutRect { }, oldContentTopAndBottomIncludingInkOverflow, repaintLogicalTop, repaintLogicalBottom);
4248 return;
4249 }
4250 }
4251
4252 if (!inlineLayout())
4253 m_lineLayout = makeUnique<LayoutIntegration::LineLayout>(*this);
4254 auto& inlineLayout = *this->inlineLayout();
4255
4256 ASSERT(containingBlock() || is<RenderView>(*this))((void)0);
4257 inlineLayout.updateFormattingContexGeometries(containingBlock() ? containingBlockLogicalWidthForContent() : LayoutUnit());
4258
4259 auto marginInfo = MarginInfo { *this, MarginInfo::IgnoreScrollbarForAfterMargin::No };
4260 auto shouldForceFullLayout = relayoutChildren == RelayoutChildren::Yes || inlineContentStatus.hasDirtyInFlowBlockLevelElement ? LayoutIntegration::LineLayout::ForceFullLayout::Yes : LayoutIntegration::LineLayout::ForceFullLayout::No;
4261 auto partialRepaintRect = inlineLayout.layout(marginInfo, shouldForceFullLayout);
4262
4263 auto contentBoxHeight = [&]() -> LayoutUnit {
4264 if (auto clampedContentHeight = updateLineClampStateAndLogicalHeightAfterLayout())
4265 return *clampedContentHeight;
4266 if (hasContentfulInlineOrBlockLine())
4267 return inlineLayout.contentLogicalHeight();
4268 if (hasLineIfEmpty())
4269 return lineHeight();
4270 return { };
4271 };
4272 auto borderBoxLogicalHeight = handleAfterSideOfBlock(marginInfo, contentBoxHeight());
4273 setLogicalHeight(borderBoxLogicalHeight);
4274 updateRepaintTopAndBottomAfterLayout(relayoutChildren, partialRepaintRect, oldContentTopAndBottomIncludingInkOverflow, repaintLogicalTop, repaintLogicalBottom);
4275
4276 if (CheckedPtr cache = protect(document())->existingAXObjectCache())
4277 cache->onLaidOutInlineContent(*this);
4278}
4279
4280void RenderBlockFlow::setStaticPositionsForSimpleOutOfFlowContent()
4281{
4282 ASSERT(childrenInline())((void)0);
4283#ifndef NDEBUG1
4284 ASSERT(!hasLineIfEmpty())((void)0);
4285 for (auto walker = InlineWalker(*this); !walker.atEnd(); walker.advance()) {
4286 if (walker.current()->style().display().isInlineType()) {
4287 ASSERT(hasSimpleStaticPositionForInlineLevelOutOfFlowChildrenByStyle(style()))((void)0);
4288 break;
4289 }
4290 }
4291#endif
4292 // We have nothing but out-of-flow boxes so we don't need to run the actual line layout.
4293 // Instead, we can just set the static positions to the point where all these boxes would end up.
4294 // This is a common case when using transforms to animate positioned boxes.
4295 auto staticPosition = LayoutPoint { borderAndPaddingStart(), borderAndPaddingBefore() };
4296
4297 for (auto walker = InlineWalker(*this); !walker.atEnd(); walker.advance()) {
4298 auto& renderer = downcast<RenderBox>(*walker.current());
4299 CheckedRef layer = *renderer.layer();
4300
4301 ASSERT(renderer.isOutOfFlowPositioned())((void)0);
4302
4303 auto previousStaticPosition = LayoutPoint { layer->staticInlinePosition(), layer->staticBlockPosition() };
4304 auto delta = staticPosition - previousStaticPosition;
4305 auto hasStaticInlinePositioning = renderer.style().hasStaticInlinePosition(isHorizontalWritingMode());
4306
4307 layer->setStaticInlinePosition(staticPosition.x());
4308 layer->setStaticBlockPosition(staticPosition.y());
4309
4310 if (!delta.isZero() && hasStaticInlinePositioning)
4311 renderer.setChildNeedsLayout(MarkOnlyThis);
4312 }
4313}
4314
4315#if ENABLE(TREE_DEBUGGING)(defined ENABLE_TREE_DEBUGGING && ENABLE_TREE_DEBUGGING
)
4316void RenderBlockFlow::outputFloatingObjects(WTF::TextStream& stream, int depth) const
4317{
4318 if (!floatingObjectSet())
4319 return;
4320
4321 for (auto& floatingObject : *floatingObjectSet()) {
4322 int printedCharacters = 0;
4323 while (++printedCharacters <= depth * 2)
4324 stream << " ";
4325
4326 stream << " ";
4327 stream << "floating object " << *floatingObject;
4328 stream.nextLine();
4329 }
4330}
4331
4332void RenderBlockFlow::outputLineTreeAndMark(WTF::TextStream& stream, const LegacyInlineBox* markedBox, int depth) const
4333{
4334 if (auto* inlineLayout = this->inlineLayout()) {
4335 inlineLayout->outputLineTree(stream, depth);
4336 return;
4337 }
4338 if (auto* root = legacyRootBox())
4339 root->outputLineTreeAndMark(stream, markedBox, depth);
4340}
4341#endif
4342
4343RenderBlockFlowRareData& RenderBlockFlow::ensureRareBlockFlowData()
4344{
4345 if (hasRareBlockFlowData())
4346 return *m_rareBlockFlowData;
4347 materializeRareBlockFlowData();
4348 return *m_rareBlockFlowData;
4349}
4350
4351void RenderBlockFlow::materializeRareBlockFlowData()
4352{
4353 ASSERT(!hasRareBlockFlowData())((void)0);
4354 m_rareBlockFlowData = makeUnique<RenderBlockFlowRareData>(*this);
4355}
4356
4357#if ENABLE(TEXT_AUTOSIZING)(defined 1 && 1)
4358
4359static inline bool isVisibleRenderText(const RenderObject& renderer)
4360{
4361 auto* renderText = dynamicDowncast<RenderText>(renderer);
4362 if (!renderText)
4363 return false;
4364
4365 return !renderText->linesBoundingBox().isEmpty() && !renderText->text().containsOnly<isASCIIWhitespace>();
4366}
4367
4368static inline bool resizeTextPermitted(const RenderObject& renderer)
4369{
4370 // We disallow resizing for text input fields and textarea to address <rdar://problem/5792987> and <rdar://problem/8021123>
4371 for (auto* ancestor = renderer.parent(); ancestor; ancestor = ancestor->parent()) {
4372 // Get the first non-shadow HTMLElement and see if it's an input.
4373 if (RefPtr element = dynamicDowncast<HTMLElement>(ancestor->element()); element && !element->isInShadowTree())
4374 return !is<HTMLInputElement>(*element) && !is<HTMLTextAreaElement>(*element);
4375 }
4376 return true;
4377}
4378
4379static bool NODELETE[[clang::annotate_type("webkit.nodelete")]] isNonBlocksOrNonFixedHeightListItems(const RenderObject& renderer)
4380{
4381 if (!renderer.isRenderBlock())
4382 return true;
4383 if (CheckedPtr renderListItem = dynamicDowncast<RenderListItem>(renderer))
4384 return !renderListItem->style().height().isFixed();
4385 return false;
4386}
4387
4388// For now, we auto size single lines of text the same as multiple lines.
4389// We've been experimenting with low values for single lines of text.
4390static inline float oneLineTextMultiplier(RenderObject& renderer, float specifiedSize)
4391{
4392 const float coefficient = renderer.settings().oneLineTextMultiplierCoefficient();
4393 return std::max((1.0f / log10f(specifiedSize) * coefficient), 1.0f);
4394}
4395
4396static inline float textMultiplier(RenderObject& renderer, float specifiedSize)
4397{
4398 const float coefficient = renderer.settings().multiLineTextMultiplierCoefficient();
4399 return std::max((1.0f / log10f(specifiedSize) * coefficient), 1.0f);
4400}
4401
4402void RenderBlockFlow::adjustComputedFontSizes(float size, float visibleWidth)
4403{
4404 LOG(TextAutosizing, "RenderBlockFlow %p adjustComputedFontSizes, size=%f visibleWidth=%f, width()=%f. Bailing: %d", this, size, visibleWidth, width().toFloat(), visibleWidth >= width())((void)0);
4405
4406 // Don't do any work if the block is smaller than the visible area.
4407 if (visibleWidth >= width())
4408 return;
4409
4410 unsigned lineCount = m_lineCountForTextAutosizing;
4411 if (lineCount == NOT_SET) {
4412 if (style().usedVisibility() != Visibility::Visible)
4413 lineCount = NO_LINE;
4414 else {
4415 size_t lineCountInBlock = 0;
4416 if (childrenInline())
4417 lineCountInBlock = this->lineCount();
4418 else {
4419 for (auto& listItem : childrenOfType<RenderListItem>(*this)) {
4420 if (!listItem.childrenInline() || listItem.style().usedVisibility() != Visibility::Visible)
4421 continue;
4422 lineCountInBlock += listItem.lineCount();
4423 if (lineCountInBlock > 1)
4424 break;
4425 }
4426 }
4427 lineCount = !lineCountInBlock ? NO_LINE : lineCountInBlock == 1 ? ONE_LINE : MULTI_LINE;
4428 }
4429 }
4430
4431 ASSERT(lineCount != NOT_SET)((void)0);
4432 if (lineCount == NO_LINE)
4433 return;
4434
4435 float actualWidth = m_widthForTextAutosizing != -1 ? static_cast<float>(m_widthForTextAutosizing) : static_cast<float>(width());
4436 float scale = visibleWidth / actualWidth;
4437 float minFontSize = roundf(size / scale);
4438
4439 for (auto* descendant = RenderObjectTraversal::firstChild(*this); descendant; ) {
4440 if (!isNonBlocksOrNonFixedHeightListItems(*descendant)) {
4441 descendant = RenderObjectTraversal::nextSkippingChildren(*descendant, this);
4442 continue;
4443 }
4444 if (!isVisibleRenderText(*descendant) || !resizeTextPermitted(*descendant)) {
4445 descendant = RenderObjectTraversal::next(*descendant, this);
4446 continue;
4447 }
4448
4449 auto& text = downcast<RenderText>(*descendant);
4450 auto& oldStyle = text.style();
4451 auto& fontDescription = oldStyle.fontDescription();
4452 float specifiedSize = fontDescription.specifiedSize();
4453 float scaledSize = roundf(specifiedSize * scale);
4454 if (scaledSize > 0 && scaledSize < minFontSize) {
4455 // Record the width of the block and the line count the first time we resize text and use it from then on for text resizing.
4456 // This makes text resizing consistent even if the block's width or line count changes (which can be caused by text resizing itself 5159915).
4457 if (m_lineCountForTextAutosizing == NOT_SET)
4458 m_lineCountForTextAutosizing = lineCount;
4459 if (m_widthForTextAutosizing == -1)
4460 m_widthForTextAutosizing = actualWidth;
4461
4462 float lineTextMultiplier = lineCount == ONE_LINE ? oneLineTextMultiplier(text, specifiedSize) : textMultiplier(text, specifiedSize);
4463 float candidateNewSize = roundf(std::min(minFontSize, specifiedSize * lineTextMultiplier));
4464
4465 if (candidateNewSize > specifiedSize && candidateNewSize != fontDescription.computedSize() && text.textNode() && oldStyle.textSizeAdjust().isAuto())
4466 protect(document())->textAutoSizing().addTextNode(*protect(text.textNode()), candidateNewSize);
4467 }
4468
4469 descendant = RenderObjectTraversal::nextSkippingChildren(text, this);
4470 }
4471}
4472
4473#endif // ENABLE(TEXT_AUTOSIZING)
4474
4475void RenderBlockFlow::layoutExcludedChildren(RelayoutChildren relayoutChildren)
4476{
4477 RenderBlock::layoutExcludedChildren(relayoutChildren);
4478
4479 auto* fragmentedFlow = multiColumnFlow();
4480 if (!fragmentedFlow)
4481 return;
4482
4483 fragmentedFlow->setIsExcludedFromNormalLayout(true);
4484
4485 setLogicalTopForChild(*fragmentedFlow, borderAndPaddingBefore());
4486
4487 if (relayoutChildren == RelayoutChildren::Yes)
4488 fragmentedFlow->setChildNeedsLayout(MarkOnlyThis);
4489
4490 if (fragmentedFlow->needsLayout()) {
4491 for (RenderMultiColumnSet* columnSet = fragmentedFlow->firstMultiColumnSet(); columnSet; columnSet = columnSet->nextSiblingMultiColumnSet())
4492 columnSet->prepareForLayout(!fragmentedFlow->inBalancingPass());
4493
4494 fragmentedFlow->invalidateFragments(MarkOnlyThis);
4495 fragmentedFlow->setNeedsHeightsRecalculation(true);
4496 fragmentedFlow->layout();
4497 } else {
4498 // At the end of multicol layout, relayoutForPagination() is called unconditionally, but if
4499 // no children are to be laid out (e.g. fixed width with layout already being up-to-date),
4500 // we want to prevent it from doing any work, so that the column balancing machinery doesn't
4501 // kick in and trigger additional unnecessary layout passes. Actually, it's not just a good
4502 // idea in general to not waste time on balancing content that hasn't been re-laid out; we
4503 // are actually required to guarantee this. The calculation of implicit breaks needs to be
4504 // preceded by a proper layout pass, since it's layout that sets up content runs, and the
4505 // runs get deleted right after every pass.
4506 fragmentedFlow->setNeedsHeightsRecalculation(false);
4507 }
4508 determineLogicalLeftPositionForChild(*fragmentedFlow);
4509}
4510
4511void RenderBlockFlow::checkForPaginationLogicalHeightChange(RelayoutChildren& relayoutChildren, LayoutUnit& pageLogicalHeight, bool& pageLogicalHeightChanged)
4512{
4513 // If we don't use columns or flow threads, then bail.
4514 if (!isRenderFragmentedFlow() && !multiColumnFlow())
4515 return;
4516
4517 // We don't actually update any of the variables. We just subclassed to adjust our column height.
4518 if (RenderMultiColumnFlow* fragmentedFlow = multiColumnFlow()) {
4519 LayoutUnit newColumnHeight;
4520 if (hasDefiniteLogicalHeight() || view().frameView().pagination().mode != Pagination::Mode::Unpaginated) {
Call argument for 'this' parameter is uncounted and unsafe
4521 auto computedValues = computeLogicalHeight(0_lu, logicalTop());
4522 newColumnHeight = std::max<LayoutUnit>(computedValues.extent - borderAndPaddingLogicalHeight() - scrollbarLogicalHeight(), 0);
4523 if (fragmentedFlow->columnHeightAvailable() != newColumnHeight)
4524 relayoutChildren = RelayoutChildren::Yes;
4525 }
4526 fragmentedFlow->setColumnHeightAvailable(newColumnHeight);
4527 } else if (CheckedPtr fragmentedFlow = dynamicDowncast<RenderFragmentedFlow>(*this)) {
4528 // FIXME: This is a hack to always make sure we have a page logical height, if said height
4529 // is known. The page logical height thing in RenderLayoutState is meaningless for flow
4530 // thread-based pagination (page height isn't necessarily uniform throughout the flow
4531 // thread), but as long as it is used universally as a means to determine whether page
4532 // height is known or not, we need this. Page height is unknown when column balancing is
4533 // enabled and flow thread height is still unknown (i.e. during the first layout pass). When
4534 // it's unknown, we need to prevent the pagination code from assuming page breaks everywhere
4535 // and thereby eating every top margin. It should be trivial to clean up and get rid of this
4536 // hack once the old multicol implementation is gone (see also RenderView::pushLayoutStateForPagination).
4537 pageLogicalHeight = fragmentedFlow->isPageLogicalHeightKnown() ? 1_lu : 0_lu;
4538
4539 pageLogicalHeightChanged = fragmentedFlow->pageLogicalSizeChanged();
4540 }
4541}
4542
4543bool RenderBlockFlow::requiresColumns(int desiredColumnCount) const
4544{
4545 return willCreateColumns(desiredColumnCount);
4546}
4547
4548void RenderBlockFlow::setComputedColumnCountAndWidth(int count, LayoutUnit width)
4549{
4550 ASSERT(!!multiColumnFlow() == requiresColumns(count))((void)0);
4551 if (!multiColumnFlow())
4552 return;
4553 multiColumnFlow()->setColumnCountAndWidth(count, width);
4554 multiColumnFlow()->setProgressionIsInline(style().hasInlineColumnAxis());
4555 multiColumnFlow()->setProgressionIsReversed(style().columnProgression() == ColumnProgression::Reverse);
4556}
4557
4558void RenderBlockFlow::updateColumnProgressionFromStyle(const RenderStyle& style)
4559{
4560 if (!multiColumnFlow())
4561 return;
4562
4563 bool needsLayout = false;
4564 bool oldProgressionIsInline = multiColumnFlow()->progressionIsInline();
4565 bool newProgressionIsInline = style.hasInlineColumnAxis();
4566 if (oldProgressionIsInline != newProgressionIsInline) {
4567 multiColumnFlow()->setProgressionIsInline(newProgressionIsInline);
4568 needsLayout = true;
4569 }
4570
4571 bool oldProgressionIsReversed = multiColumnFlow()->progressionIsReversed();
4572 bool newProgressionIsReversed = style.columnProgression() == ColumnProgression::Reverse;
4573 if (oldProgressionIsReversed != newProgressionIsReversed) {
4574 multiColumnFlow()->setProgressionIsReversed(newProgressionIsReversed);
4575 needsLayout = true;
4576 }
4577
4578 if (needsLayout)
4579 setNeedsLayoutAndPreferredWidthsUpdate();
4580}
4581
4582LayoutUnit RenderBlockFlow::computedColumnWidth() const
4583{
4584 if (multiColumnFlow())
4585 return multiColumnFlow()->computedColumnWidth();
4586 return contentBoxLogicalWidth();
4587}
4588
4589unsigned RenderBlockFlow::computedColumnCount() const
4590{
4591 if (multiColumnFlow())
4592 return multiColumnFlow()->computedColumnCount();
4593
4594 return 1;
4595}
4596
4597LayoutOptionalOutsets RenderBlockFlow::allowedLayoutOverflow() const
4598{
4599 LayoutOptionalOutsets allowance = RenderBox::allowedLayoutOverflow();
4600
4601 if (!style().alignContent().isNormal()) {
4602 if (hasRareBlockFlowData()) {
4603 if (isHorizontalWritingMode())
4604 allowance.setTop(-rareBlockFlowData()->m_alignContentShift);
4605 else
4606 allowance.setLeft(-rareBlockFlowData()->m_alignContentShift);
4607 }
4608 }
4609
4610 if (multiColumnFlow() && style().columnProgression() != ColumnProgression::Normal) {
4611 if (isHorizontalWritingMode() ^ !style().hasInlineColumnAxis())
4612 allowance = allowance.xFlippedCopy();
4613 else
4614 allowance = allowance.yFlippedCopy();
4615 }
4616
4617 return allowance;
4618}
4619
4620struct InlineMinMaxIterator {
4621// InlineMinMaxIterator is a class that will iterate over all render objects that contribute to
4622// inline min/max width calculations. Note the following about the way it walks:
4623// (1) Positioned content is skipped (since it does not contribute to min/max width of a block)
4624// (2) We do not drill into the children of floats or replaced elements, since you can't break
4625// in the middle of such an element.
4626// (3) Inline flows (e.g., <a>, <span>, <i>) are walked twice, since each side can have
4627// distinct borders/margin/padding that contribute to the min/max width.
4628 InlineMinMaxIterator(const RenderBlockFlow& blockContainer)
4629 : m_blockContainer(blockContainer)
4630 {
4631 }
4632
4633 RenderObject* next();
4634 bool NODELETE[[clang::annotate_type("webkit.nodelete")]] isEndOfInline() const { return m_isEndOfInline; }
4635
4636private:
4637 const RenderBlockFlow& m_blockContainer;
4638
4639 RenderObject* m_current { nullptr };
4640 bool m_isEndOfInline { false };
4641 bool m_initial { true };
4642};
4643
4644RenderObject* InlineMinMaxIterator::next()
4645{
4646 RenderObject* candidate = m_initial ? m_blockContainer.firstChild() : nullptr;
4647 m_initial = false;
4648
4649 bool oldEndOfInline = m_isEndOfInline;
4650 m_isEndOfInline = false;
4651 do {
4652
4653 if (!oldEndOfInline && is<RenderInline>(m_current))
4654 candidate = m_current->firstChildSlow();
4655
4656 if (!candidate) {
4657 // We hit the end of our inline. (It was empty, e.g., <span></span>.)
4658 if (!oldEndOfInline && m_current && m_current->isRenderInline()) {
4659 candidate = m_current;
4660 m_isEndOfInline = true;
4661 break;
4662 }
4663
4664 while (m_current && m_current != &m_blockContainer) {
4665 candidate = m_current->nextSibling();
4666 if (candidate)
4667 break;
4668 m_current = m_current->parent();
4669 if (m_current && m_current != &m_blockContainer && m_current->isRenderInline()) {
4670 candidate = m_current;
4671 m_isEndOfInline = true;
4672 break;
4673 }
4674 }
4675 }
4676
4677 if (!candidate)
4678 break;
4679
4680 if (candidate->isOutOfFlowPositioned()) {
4681 m_current = candidate;
4682 candidate = nullptr;
4683 continue;
4684 }
4685
4686 if (is<RenderInline>(*candidate) || candidate->isRenderTextOrLineBreak() || candidate->isFloating() || candidate->isBlockLevelReplacedOrAtomicInline())
4687 break;
4688
4689 if (candidate->style().display().isBlockType()) {
4690 ASSERT(candidate->settings().blocksInInlineLayoutEnabled())((void)0);
4691 break;
4692 }
4693
4694 ASSERT_NOT_REACHED()((void)0);
4695 m_current = candidate;
4696 candidate = nullptr;
4697 } while (m_current || m_current == &m_blockContainer);
4698 // Update our position.
4699 m_current = candidate;
4700 return candidate;
4701}
4702
4703template <typename SizeType>
4704auto borderMarginOrPaddingWidth(LayoutUnit childValue, const SizeType& marginOrPadding, const Style::ZoomFactor& zoomFactor) -> LayoutUnit {
4705 if (auto fixed = marginOrPadding.tryFixed())
4706 return LayoutUnit(fixed->resolveZoom(zoomFactor));
4707 if constexpr (std::same_as<SizeType, Style::MarginEdge>) {
4708 if (marginOrPadding.isAuto())
4709 return { };
4710 }
4711 return childValue;
4712};
4713
4714static LayoutUnit getBorderPaddingMargin(const RenderBoxModelObject& child, bool endOfInline)
4715{
4716 auto& childStyle = child.style();
4717 const auto& childZoomFactor = childStyle.usedZoomForLength();
4718
4719 if (endOfInline) {
4720 return borderMarginOrPaddingWidth(child.marginEnd(), childStyle.marginEnd(), childZoomFactor) +
4721 borderMarginOrPaddingWidth(child.paddingEnd(), childStyle.paddingEnd(), childZoomFactor) +
4722 child.borderEnd();
4723 }
4724 return borderMarginOrPaddingWidth(child.marginStart(), childStyle.marginStart(), childZoomFactor) +
4725 borderMarginOrPaddingWidth(child.paddingStart(), childStyle.paddingStart(), childZoomFactor) +
4726 child.borderStart();
4727}
4728
4729static inline void stripTrailingSpace(float& inlineMax, float& inlineMin, RenderObject* trailingSpaceChild)
4730{
4731 if (auto* renderText = dynamicDowncast<RenderText>(trailingSpaceChild)) {
4732 // Collapse away the trailing space at the end of a block.
4733 const char16_t space = ' ';
4734 const FontCascade& font = renderText->style().fontCascade(); // FIXME: This ignores first-line.
4735 float spaceWidth = font.width(RenderBlock::constructTextRun(span(space), renderText->style()));
4736 inlineMax -= spaceWidth + font.wordSpacing();
4737 if (inlineMin > inlineMax)
4738 inlineMin = inlineMax;
4739 }
4740}
4741
4742static inline std::optional<std::pair<const RenderText&, const RenderText&>> trailingRubyBaseAndAdjacentTextContent(const RenderInline& rubyBase, const RenderBlockFlow& blockContainer)
4743{
4744 // This functions returns adjacent _content_ renderers by skipping non-inline content (floats, out-of-flow content) inline boxes and related annotation boxes.
4745 // e.g. <ruby>
4746 // <span>base</span><rt>annotation</rt>
4747 // <span>adjacent base</span><rt>annotation</rt>
4748 // </ruby>
4749 // returns "base" and "adjacent base" RenderText renderers.
4750 if (!rubyBase.firstInFlowChild())
4751 return { };
4752
4753 auto shouldSkip = [&](auto& renderer) {
4754 if (is<RenderText>(renderer))
4755 return false;
4756 if (is<RenderInline>(renderer))
4757 return true;
4758 auto& renderBox = downcast<RenderBoxModelObject>(renderer);
4759 return !renderBox.isInFlow() || renderBox.style().display() == Style::DisplayType::RubyText;
4760 };
4761
4762 auto walker = InlineWalker(blockContainer, rubyBase.firstInFlowChild());
4763 auto lastInlineChildOfRubyBase = [&]() -> RenderObject* {
4764 RenderObject* lastChild = nullptr;
4765 for (; !walker.atEnd(); walker.advance()) {
4766 auto* renderer = walker.current();
4767 if (renderer->parent() == rubyBase.parent())
4768 return lastChild;
4769 if (!shouldSkip(*renderer))
4770 lastChild = renderer;
4771 }
4772 return { };
4773 };
4774 auto* lastCHild = lastInlineChildOfRubyBase();
4775 if (!lastCHild || !is<RenderText>(*lastCHild))
4776 return { };
4777
4778 auto firstInlineAfterRubyBase = [&]() -> RenderObject* {
4779 for (; !walker.atEnd(); walker.advance()) {
4780 if (!shouldSkip(*walker.current()))
4781 return walker.current();
4782 }
4783 return { };
4784 };
4785 auto* firstSibling = firstInlineAfterRubyBase();
4786 if (!firstSibling || !is<RenderText>(*firstSibling))
4787 return { };
4788
4789 return { std::pair<const RenderText&, const RenderText&> { downcast<RenderText>(*lastCHild), downcast<RenderText>(*firstSibling) } };
4790}
4791
4792static inline bool hasTrailingSoftWrapOpportunity(const RenderInline& rubyBase, const RenderBlockFlow& blockContainer)
4793{
4794 if (rubyBase.parent()->style().textWrapMode() == TextWrapMode::NoWrap)
4795 return false;
4796
4797 if (auto lastAndNextTextContent = trailingRubyBaseAndAdjacentTextContent(rubyBase, blockContainer))
4798 return Layout::TextUtil::mayBreakInBetween(lastAndNextTextContent->first.text(), lastAndNextTextContent->first.style(), lastAndNextTextContent->second.text(), lastAndNextTextContent->second.style());
4799 return false;
4800}
4801
4802static inline LayoutUnit preferredWidth(LayoutUnit preferredWidth, float result)
4803{
4804 return std::max(preferredWidth, LayoutUnit::fromFloatCeil(result));
4805}
4806
4807static inline std::optional<LayoutUnit> textIndentForBlockContainer(const RenderBlockFlow& renderer)
4808{
4809 auto& style = renderer.style();
4810 if (auto fixedTextIndent = style.textIndent().length.tryFixed())
4811 return !fixedTextIndent->isZero() ? std::make_optional(LayoutUnit { fixedTextIndent->resolveZoom(style.usedZoomForLength()) }) : std::nullopt;
4812
4813 auto indentValue = LayoutUnit { };
4814 if (auto* containingBlock = renderer.containingBlock()) {
4815 if (auto containingBlockFixedLogicalWidth = containingBlock->style().logicalWidth().tryFixed()) {
4816 auto containingBlockFixedLogicalWidthValue = Style::evaluate<LayoutUnit>(*containingBlockFixedLogicalWidth, containingBlock->style().usedZoomForLength());
4817 // At this point of the shrink-to-fit computation, we don't have a used value for the containing block width
4818 // (that's exactly to what we try to contribute here) unless the computed value is fixed.
4819 indentValue = Style::evaluate<LayoutUnit>(style.textIndent().length, containingBlockFixedLogicalWidthValue, containingBlock->style().usedZoomForLength());
4820 }
4821 }
4822 return indentValue ? std::make_optional(indentValue) : std::nullopt;
4823}
4824
4825void RenderBlockFlow::computeInlinePreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
4826{
4827 ASSERT(!shouldApplyInlineSizeContainment())((void)0);
4828
4829 if (const_cast<RenderBlockFlow&>(*this).tryComputePreferredWidthsUsingInlinePath(minLogicalWidth, maxLogicalWidth))
4830 return;
4831
4832 float inlineMax = 0.f;
4833 float inlineMin = 0.f;
4834
4835 const RenderStyle& styleToUse = style();
4836 // If we are at the start of a line, we want to ignore all white-space.
4837 // Also strip spaces if we previously had text that ended in a trailing space.
4838 bool stripFrontSpaces = true;
4839 RenderObject* trailingSpaceChild = nullptr;
4840
4841 // Firefox and Opera will allow a table cell to grow to fit an image inside it under
4842 // very specific cirucumstances (in order to match common WinIE renderings).
4843 // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.)
4844 bool allowImagesToBreak = !document().inQuirksMode() || !isRenderTableCell() || !styleToUse.logicalWidth().isIntrinsicOrLegacyIntrinsicOrAuto();
4845
4846 bool oldAutoWrap = styleToUse.textWrapMode() != TextWrapMode::NoWrap;
4847
4848 InlineMinMaxIterator childIterator(*this);
4849
4850 // Signals the text indent was more negative than the min preferred width
4851 auto remainingNegativeTextIndent = std::optional<LayoutUnit> { };
4852 auto textIndentForMinimum = textIndentForBlockContainer(*this);
4853 auto textIndentForMaximum = textIndentForMinimum;
4854 CheckedPtr<RenderBox> previousFloat;
4855 bool isPrevChildInlineFlow = false;
4856 bool shouldBreakLineAfterText = false;
4857 bool canHangPunctuationAtStart = styleToUse.hangingPunctuation().contains(Style::HangingPunctuationValue::First);
4858 bool canHangPunctuationAtEnd = styleToUse.hangingPunctuation().contains(Style::HangingPunctuationValue::Last);
4859 RenderText* lastText = nullptr;
4860 struct RubyBaseContent {
4861 float minimumWidth { 0.f };
4862 float maxiumumWidth { 0.f };
4863 bool hasBreakingPositionAfter { false };
4864 };
4865 Vector<RubyBaseContent> rubyBaseContentStack;
4866
4867 bool addedStartPunctuationHang = false;
4868
4869 while (RenderObject* child = childIterator.next()) {
4870 // Interlinear annotations don't participate in inline layout, but they put a minimum width requirement on the associated ruby base.
4871 auto isInterlinearTypeAnnotation = [&] {
4872 if (CheckedPtr renderBlock = dynamicDowncast<RenderBlock>(*child)) {
4873 auto& style = renderBlock->style();
4874 return style.display() == Style::DisplayType::RubyText && (!style.isInterCharacterRubyPosition() || styleToUse.writingMode().isVerticalTypographic());
4875 }
4876 return false;
4877 };
4878 if (isInterlinearTypeAnnotation()) {
4879 auto annotationMinimumIntrinsicWidth = LayoutUnit { };
4880 auto annotationMaximumIntrinsicWidth = LayoutUnit { };
4881 computeChildPreferredLogicalWidths(downcast<RenderBlock>(*child), annotationMinimumIntrinsicWidth, annotationMaximumIntrinsicWidth);
4882
4883 if (!rubyBaseContentStack.isEmpty()) {
4884 // Annotation box is always preceded by the associated ruby base.
4885 // inlineMin/max only gets expanded if the annotation is wider than the base content is.
4886 auto baseContent = rubyBaseContentStack.takeLast();
4887 inlineMax += std::max(0.f, annotationMaximumIntrinsicWidth.ceilToFloat() - baseContent.maxiumumWidth);
4888 if (baseContent.hasBreakingPositionAfter) {
4889 // When base end has breaking position, the inlineMin value is already reset as we are not tracking the inline content for this "line" anymore.
4890 // However the annotation still belows to the current "line" so we have to update the minLogicalWidth in case annotation is wider than the base content.
4891 minLogicalWidth += std::max(0.f, annotationMinimumIntrinsicWidth.ceilToFloat() - baseContent.minimumWidth);
4892 } else
4893 inlineMin += std::max(0.f, annotationMinimumIntrinsicWidth.ceilToFloat() - baseContent.minimumWidth);
4894 } else
4895 ASSERT_NOT_REACHED()((void)0);
4896 continue;
4897 }
4898
4899 auto resetLineForForcedLineBreak = [&] {
4900 if (styleToUse.collapseWhiteSpace())
4901 stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild);
4902 minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin);
4903 maxLogicalWidth = preferredWidth(maxLogicalWidth, inlineMax);
4904 inlineMin = 0;
4905 inlineMax = 0;
4906 stripFrontSpaces = true;
4907 trailingSpaceChild = 0;
4908 textIndentForMinimum = { };
4909 textIndentForMaximum = { };
4910 remainingNegativeTextIndent = { };
4911 addedStartPunctuationHang = true;
4912 isPrevChildInlineFlow = false;
4913 oldAutoWrap = child->parent()->style().textWrapMode() != TextWrapMode::NoWrap;
4914 };
4915
4916 if (child->isBR()) {
4917 resetLineForForcedLineBreak();
4918 continue;
4919 }
4920
4921 if (child->style().display().isBlockType() && !child->isFloating() && is<RenderBox>(*child)) {
4922 ASSERT(settings().blocksInInlineLayoutEnabled())((void)0);
4923
4924 resetLineForForcedLineBreak();
4925
4926 auto blockMinWidth = LayoutUnit { };
4927 auto blocMaxWidth = LayoutUnit { };
4928 computeChildPreferredLogicalWidths(downcast<RenderBox>(*child), blockMinWidth, blocMaxWidth);
4929
4930 auto marginsInInlineDirection = LayoutUnit { };
4931 if (auto fixedMarginStart = child->style().marginStart(writingMode()).tryFixed())
4932 marginsInInlineDirection += LayoutUnit::fromFloatCeil(fixedMarginStart->resolveZoom(child->style().usedZoomForLength()));
4933 if (auto fixedMarginEnd = child->style().marginEnd(writingMode()).tryFixed())
4934 marginsInInlineDirection += LayoutUnit::fromFloatCeil(fixedMarginEnd->resolveZoom(child->style().usedZoomForLength()));
4935
4936 minLogicalWidth = std::max(minLogicalWidth, blockMinWidth + marginsInInlineDirection);
4937 maxLogicalWidth = std::max(maxLogicalWidth, blocMaxWidth + marginsInInlineDirection);
4938 continue;
4939 }
4940
4941 // Step One: determine whether or not we need to terminate our current line.
4942 // Each discrete chunk can become the new min-width, if it is the widest chunk
4943 // seen so far, and it can also become the max-width.
4944
4945 // Children fall into three categories:
4946 // (1) An inline flow object. These objects always have a min/max of 0,
4947 // and are included in the iteration solely so that their margins can
4948 // be added in.
4949 //
4950 // (2) An inline non-text non-flow object, e.g., an inline replaced element.
4951 // These objects can always be on a line by themselves, so in this situation
4952 // we need to break the current line, and then add in our own margins and min/max
4953 // width on its own line, and then terminate the line.
4954 //
4955 // (3) A text object. Text runs can have breakable characters at the start,
4956 // the middle or the end. They may also lose whitespace off the front if
4957 // we're already ignoring whitespace. In order to compute accurate min-width
4958 // information, we need three pieces of information.
4959 // (a) the min-width of the first non-breakable run. Should be 0 if the text string
4960 // starts with whitespace.
4961 // (b) the min-width of the last non-breakable run. Should be 0 if the text string
4962 // ends with whitespace.
4963 // (c) the min/max width of the string (trimmed for whitespace).
4964 //
4965 // If the text string starts with whitespace, then we need to terminate our current line
4966 // (unless we're already in a whitespace stripping mode.
4967 //
4968 // If the text string has a breakable character in the middle, but didn't start
4969 // with whitespace, then we add the width of the first non-breakable run and
4970 // then end the current line. We then need to use the intermediate min/max width
4971 // values (if any of them are larger than our current min/max). We then look at
4972 // the width of the last non-breakable run and use that to start a new line
4973 // (unless we end in whitespace).
4974 auto autoWrap = child->isBlockLevelReplacedOrAtomicInline() || is<RenderText>(*child) ? (child->parent()->style().textWrapMode() != TextWrapMode::NoWrap) : (child->style().textWrapMode() != TextWrapMode::NoWrap);
4975 auto childMin = 0.f;
4976 auto childMax = 0.f;
4977
4978 if (!child->isRenderText()) {
4979 if (child->isLineBreakOpportunity()) {
4980 minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin);
4981 inlineMin = 0;
4982 continue;
4983 }
4984 auto& childStyle = downcast<RenderElement>(*child).style();
4985 // Case (1) and (2). Inline replaced and inline flow elements.
4986 if (CheckedPtr renderInline = dynamicDowncast<RenderInline>(*child)) {
4987 // Add in padding/border/margin from the appropriate side of
4988 // the element.
4989 float bpm = getBorderPaddingMargin(*renderInline, childIterator.isEndOfInline());
4990 childMin += bpm;
4991 childMax += bpm;
4992
4993 if (childStyle.display() == Style::DisplayType::RubyBase && !childIterator.isEndOfInline())
4994 rubyBaseContentStack.append({ inlineMin, inlineMax, false });
4995
4996 inlineMin += childMin;
4997 inlineMax += childMax;
4998
4999 if (childStyle.display() == Style::DisplayType::RubyBase && childIterator.isEndOfInline()) {
5000 if (!rubyBaseContentStack.isEmpty()) {
5001 auto rubyBaseStart = rubyBaseContentStack.last();
5002 auto baseHasBreakingPositionAfter = hasTrailingSoftWrapOpportunity(*renderInline, *this);
5003 rubyBaseContentStack.last() = RubyBaseContent { inlineMin - rubyBaseStart.minimumWidth, inlineMax - rubyBaseStart.maxiumumWidth, baseHasBreakingPositionAfter };
5004 if (baseHasBreakingPositionAfter) {
5005 // Let's mark based end as a breaking opportunity. Note that annotation may chage the final value of minLogicalWidth.
5006 minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin);
5007 inlineMin = 0;
5008 }
5009 } else
5010 ASSERT_NOT_REACHED()((void)0);
5011 }
5012
5013 child->clearNeedsPreferredWidthsUpdate();
5014 } else {
5015 const auto& childZoomFactor = childStyle.usedZoomForLength();
5016
5017 // Inline replaced boxes add in their margins to their min/max values.
5018 if (!child->isFloating())
5019 lastText = nullptr;
5020 LayoutUnit margins;
5021 if (auto fixedMarginStart = childStyle.marginStart(writingMode()).tryFixed())
5022 margins += LayoutUnit::fromFloatCeil(fixedMarginStart->resolveZoom(childZoomFactor));
5023 if (auto fixedMarginEnd = childStyle.marginEnd(writingMode()).tryFixed())
5024 margins += LayoutUnit::fromFloatCeil(fixedMarginEnd->resolveZoom(childZoomFactor));
5025 childMin += margins.ceilToFloat();
5026 childMax += margins.ceilToFloat();
5027 }
5028 }
5029
5030 if (!is<RenderInline>(*child) && !is<RenderText>(*child)) {
5031 // Case (2). Inline replaced boxes and floats.
5032 // Terminate the current line as far as minwidth is concerned.
5033 LayoutUnit childMinPreferredLogicalWidth;
5034 LayoutUnit childMaxPreferredLogicalWidth;
5035 CheckedPtr box = dynamicDowncast<RenderBox>(*child);
5036 if (box->isHorizontalWritingMode() != isHorizontalWritingMode()) {
5037 auto extent = box->computeLogicalHeight(box->borderAndPaddingLogicalHeight(), 0).extent;
5038 childMinPreferredLogicalWidth = extent;
5039 childMaxPreferredLogicalWidth = extent;
5040 } else
5041 computeChildPreferredLogicalWidths(*box, childMinPreferredLogicalWidth, childMaxPreferredLogicalWidth);
5042 childMin += childMinPreferredLogicalWidth.ceilToFloat();
5043 childMax += childMaxPreferredLogicalWidth.ceilToFloat();
5044
5045 bool clearPreviousFloat = false;
5046 if (box->isFloating()) {
5047 auto childClearValue = RenderStyle::usedClear(*box);
5048 if (previousFloat) {
5049 auto previousFloatValue = RenderStyle::usedFloat(*previousFloat);
5050 clearPreviousFloat =
5051 (previousFloatValue == UsedFloat::Left && (childClearValue == UsedClear::Left || childClearValue == UsedClear::Both))
5052 || (previousFloatValue == UsedFloat::Right && (childClearValue == UsedClear::Right || childClearValue == UsedClear::Both));
5053 }
5054 previousFloat = box;
5055 }
5056
5057 bool canBreakReplacedElement = !box->isImage() || allowImagesToBreak;
5058 if (((canBreakReplacedElement && (autoWrap || oldAutoWrap) && (!isPrevChildInlineFlow || shouldBreakLineAfterText)) || clearPreviousFloat)) {
5059 minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin);
5060 inlineMin = 0;
5061 }
5062
5063 // If we're supposed to clear the previous float, then terminate maxwidth as well.
5064 if (clearPreviousFloat) {
5065 maxLogicalWidth = preferredWidth(maxLogicalWidth, inlineMax);
5066 inlineMax = 0;
5067 }
5068
5069 // Add in text-indent. This is added in only once.
5070 if (!box->isFloating()) {
5071 if (textIndentForMinimum) {
5072 childMin += LayoutUnit { textIndentForMinimum->ceilToFloat() };
5073 textIndentForMinimum = childMin < 0 ? std::make_optional(LayoutUnit::fromFloatCeil(childMin)) : std::nullopt;
5074 }
5075
5076 if (textIndentForMaximum) {
5077 childMax += LayoutUnit { textIndentForMaximum->ceilToFloat() };
5078 textIndentForMaximum = childMax < 0 ? std::make_optional(LayoutUnit::fromFloatCeil(childMax)) : std::nullopt;
5079 }
5080 }
5081
5082 if (canHangPunctuationAtStart && !addedStartPunctuationHang && !box->isFloating())
5083 addedStartPunctuationHang = true;
5084
5085 // Add our width to the max.
5086 inlineMax += std::max<float>(0, childMax);
5087
5088 if ((!autoWrap || !canBreakReplacedElement || (isPrevChildInlineFlow && !shouldBreakLineAfterText))) {
5089 if (box->isFloating())
5090 minLogicalWidth = preferredWidth(minLogicalWidth, childMin);
5091 else
5092 inlineMin += childMin;
5093 } else {
5094 // Now check our line.
5095 minLogicalWidth = preferredWidth(minLogicalWidth, childMin);
5096
5097 // Now start a new line.
5098 inlineMin = 0;
5099 }
5100
5101 if (autoWrap && canBreakReplacedElement && isPrevChildInlineFlow) {
5102 minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin);
5103 inlineMin = 0;
5104 }
5105
5106 // We are no longer stripping whitespace at the start of a line.
5107 if (!box->isFloating()) {
5108 stripFrontSpaces = false;
5109 trailingSpaceChild = nullptr;
5110 lastText = nullptr;
5111 }
5112 } else if (CheckedPtr renderText = dynamicDowncast<RenderText>(*child)) {
5113 if (renderText->style().textCombine() != TextCombine::None) {
5114 if (CheckedPtr renderCombineText = dynamicDowncast<RenderCombineText>(*renderText))
5115 renderCombineText->combineTextIfNeeded();
5116 }
5117
5118 // Determine if we have a breakable character. Pass in
5119 // whether or not we should ignore any spaces at the front
5120 // of the string. If those are going to be stripped out,
5121 // then they shouldn't be considered in the breakable char
5122 // check.
5123 bool strippingBeginWS = stripFrontSpaces;
5124 auto widths = renderText->trimmedPreferredWidths(inlineMax, stripFrontSpaces);
5125
5126 childMin = widths.min;
5127 childMax = widths.max;
5128
5129 // This text object will not be rendered, but it may still provide a breaking opportunity.
5130 if (!widths.hasBreak && !childMax) {
5131 if (autoWrap && (widths.beginWS || widths.endWS || widths.endZeroSpace)) {
5132 minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin);
5133 inlineMin = 0;
5134 }
5135 continue;
5136 }
5137
5138 lastText = renderText.get();
5139
5140 if (stripFrontSpaces)
5141 trailingSpaceChild = child;
5142 else
5143 trailingSpaceChild = 0;
5144
5145 // Add in text-indent. This is added in only once.
5146 float ti = 0.f;
5147 if (textIndentForMinimum || remainingNegativeTextIndent) {
5148 ti = (textIndentForMinimum ? *textIndentForMinimum : *remainingNegativeTextIndent).ceilToFloat();
5149 childMin += ti;
5150 widths.beginMin += ti;
5151 // It the text indent negative and larger than the child minimum, we re-use the remainder
5152 // in future minimum calculations, but using the negative value again on the maximum
5153 // will lead to under-counting the max pref width.
5154 textIndentForMinimum = { };
5155 remainingNegativeTextIndent = childMin < 0 ? std::make_optional(childMin) : std::nullopt;
5156 }
5157
5158 if (textIndentForMaximum) {
5159 auto textIndent = textIndentForMaximum->ceilToFloat();
5160 childMax += textIndent;
5161 widths.beginMax += textIndent;
5162 textIndentForMaximum = { };
5163 }
5164
5165 // See if we have a hanging punctuation situation at the start.
5166 if (canHangPunctuationAtStart && !addedStartPunctuationHang) {
5167 unsigned startIndex = strippingBeginWS ? renderText->firstCharacterIndexStrippingSpaces() : 0;
5168 float hangStartWidth = renderText->hangablePunctuationStartWidth(startIndex);
5169 childMin -= hangStartWidth;
5170 widths.beginMin -= hangStartWidth;
5171 childMax -= hangStartWidth;
5172 widths.beginMax -= hangStartWidth;
5173 addedStartPunctuationHang = true;
5174 }
5175
5176 // If we have no breakable characters at all,
5177 // then this is the easy case. We add ourselves to the current
5178 // min and max and continue.
5179 if (!widths.hasBreakableChar)
5180 inlineMin += childMin;
5181 else {
5182 // We have a breakable character. Now we need to know if
5183 // we start and end with whitespace.
5184 if (widths.beginWS) {
5185 // End the current line.
5186 minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin);
5187 } else {
5188 inlineMin += widths.beginMin;
5189 minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin);
5190 childMin -= ti;
5191 }
5192
5193 inlineMin = childMin;
5194
5195 if (widths.endWS || widths.endZeroSpace) {
5196 // We end in breakable space, which means we can end our current line.
5197 minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin);
5198 inlineMin = 0;
5199 shouldBreakLineAfterText = false;
5200 } else {
5201 minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin);
5202 inlineMin = widths.endMin;
5203 shouldBreakLineAfterText = true;
5204 }
5205 }
5206
5207 if (widths.hasBreak) {
5208 inlineMax += widths.beginMax;
5209 maxLogicalWidth = preferredWidth(maxLogicalWidth, inlineMax);
5210 maxLogicalWidth = preferredWidth(maxLogicalWidth, childMax);
5211 inlineMax = widths.endMax;
5212 textIndentForMinimum = { };
5213 textIndentForMaximum = { };
5214 remainingNegativeTextIndent = { };
5215 addedStartPunctuationHang = true;
5216 if (widths.endsWithBreak)
5217 stripFrontSpaces = true;
5218
5219 } else
5220 inlineMax += std::max<float>(0, childMax);
5221 }
5222
5223 // Ignore spaces after a list marker.
5224 if (child->isRenderListMarker())
5225 stripFrontSpaces = true;
5226
5227 isPrevChildInlineFlow = !child->isRenderText() && child->isRenderInline();
5228 oldAutoWrap = autoWrap;
5229 }
5230
5231 if (styleToUse.collapseWhiteSpace())
5232 stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild);
5233
5234 if (canHangPunctuationAtEnd && lastText && lastText->text().length() > 0) {
5235 unsigned endIndex = trailingSpaceChild == lastText ? lastText->lastCharacterIndexStrippingSpaces() : lastText->text().length() - 1;
5236 float endHangWidth = lastText->hangablePunctuationEndWidth(endIndex);
5237 inlineMin -= endHangWidth;
5238 inlineMax -= endHangWidth;
5239 }
5240
5241 minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin);
5242 maxLogicalWidth = preferredWidth(maxLogicalWidth, inlineMax);
5243}
5244
5245bool RenderBlockFlow::tryComputePreferredWidthsUsingInlinePath(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth)
5246{
5247 if (!firstInFlowChild())
5248 return false;
5249
5250 computeAndSetLineLayoutPath();
5251
5252 if (lineLayoutPath() != InlinePath)
5253 return false;
5254
5255 if (!LayoutIntegration::LineLayout::canUseForPreferredWidthComputation(*this))
5256 return false;
5257
5258 if (!inlineLayout())
5259 m_lineLayout = makeUnique<LayoutIntegration::LineLayout>(*this);
5260
5261 std::tie(minLogicalWidth, maxLogicalWidth) = inlineLayout()->computeIntrinsicWidthConstraints();
5262 for (auto walker = InlineWalker(*this); !walker.atEnd(); walker.advance()) {
5263 auto* renderer = walker.current();
5264 renderer->clearNeedsPreferredWidthsUpdate();
5265 if (auto* renderText = dynamicDowncast<RenderText>(renderer))
5266 renderText->resetMinMaxWidth();
5267 }
5268 return true;
5269}
5270
5271}
5272// namespace WebCore