Bug Summary

File:Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/rendering/RenderBlock.cpp
Warning:line 483, column 25
A function 'isDelayingUpdateScrollInfoAfterLayout' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object

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 UnifiedSource409.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/UnifiedSource409.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/UnifiedSource409.plist -x c++ /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebCore/unified-sources/UnifiedSource409.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-2025 Apple Inc. All rights reserved.
6 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
7 * Copyright (C) 2026 Samuel Weinig <sam@webkit.org>
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 "RenderBlock.h"
27
28#include "AXObjectCache.h"
29#include "BorderShape.h"
30#include "ContainerNodeInlines.h"
31#include "Editor.h"
32#include "Element.h"
33#include "ElementInlines.h"
34#include "EventRegion.h"
35#include "EventTargetInlines.h"
36#include "FloatQuad.h"
37#include "FrameSelection.h"
38#include "GraphicsContext.h"
39#include "HTMLNames.h"
40#include "HTMLTextFormControlElement.h"
41#include "HitTestLocation.h"
42#include "HitTestResult.h"
43#include "ImageBuffer.h"
44#include "InlineIteratorInlineBox.h"
45#include "LayoutRepainter.h"
46#include "LayoutScope.h"
47#include "LocalFrame.h"
48#include "LocalFrameView.h"
49#include "Logging.h"
50#include "LogicalSelectionOffsetCachesInlines.h"
51#include "OutlinePainter.h"
52#include "Page.h"
53#include "PaintInfo.h"
54#include "PaintInfoInlines.h"
55#include "RenderBlockFlow.h"
56#include "RenderBlockInlines.h"
57#include "RenderBoxFragmentInfo.h"
58#include "RenderBoxInlines.h"
59#include "RenderButton.h"
60#include "RenderChildIterator.h"
61#include "RenderCombineText.h"
62#include "RenderDeprecatedFlexibleBox.h"
63#include "RenderElementStyleInlines.h"
64#include "RenderFlexibleBox.h"
65#include "RenderFragmentedFlow.h"
66#include "RenderGrid.h"
67#include "RenderInline.h"
68#include "RenderIterator.h"
69#include "RenderLayer.h"
70#include "RenderLayerScrollableArea.h"
71#include "RenderLayoutState.h"
72#include "RenderListMarker.h"
73#include "RenderMenuList.h"
74#include "RenderObjectInlines.h"
75#include "RenderTableCell.h"
76#include "RenderTextControl.h"
77#include "RenderTextFragment.h"
78#include "RenderTheme.h"
79#include "RenderTreeBuilder.h"
80#include "RenderTreePosition.h"
81#include "RenderView.h"
82#include "Settings.h"
83#include "ShadowRoot.h"
84#include "ShapeOutsideInfo.h"
85#include "StyleContainmentCheckerInlines.h"
86#include "TransformState.h"
87#include <wtf/NeverDestroyed.h>
88#include <wtf/SetForScope.h>
89#include <wtf/StackStats.h>
90#include <wtf/TZoneMallocInlines.h>
91#include <wtf/text/TextStream.h>
92
93namespace WebCore {
94
95using namespace HTMLNames;
96using namespace WTF::Unicode;
97
98WTF_MAKE_TZONE_ALLOCATED_IMPL(RenderBlock)::bmalloc::api::HeapRef RenderBlock::s_heapRef; const TZoneSpecification
RenderBlock::s_heapSpec = { &RenderBlock::s_heapRef, TZoneSpecification
::encodeSize<RenderBlock>(), TZoneSpecification::encodeAlignment
<RenderBlock>(), TZoneSpecification::encodeCategory<
RenderBlock>(), ::bmalloc::api::compactAllocationMode<RenderBlock
>(), TZoneSpecification::encodeDescriptor<RenderBlock>
(), }; void* RenderBlock::operatorNewSlow(size_t size) { if constexpr
(::bmalloc::api::compactAllocationMode<RenderBlock>() ==
CompactAllocationMode::Compact) return ::bmalloc::api::tzoneAllocateCompactSlow
(size, s_heapSpec); return ::bmalloc::api::tzoneAllocateNonCompactSlow
(size, s_heapSpec); } using __makeBtzoneMallocedInlineMacroSemicolonifier
__attribute__((unused)) = int
;
99
100struct SameSizeAsRenderBlock : public RenderBox {
101};
102
103static_assert(sizeof(RenderBlock) == sizeof(SameSizeAsRenderBlock), "RenderBlock should stay small");
104
105using TrackedDescendantsMap = SingleThreadWeakHashMap<const RenderBlock, std::unique_ptr<TrackedRendererListHashSet>>;
106using TrackedContainerMap = SingleThreadWeakHashMap<const RenderBox, SingleThreadWeakHashSet<const RenderBlock>>;
107
108static TrackedDescendantsMap* percentHeightDescendantsMap;
109static TrackedContainerMap* percentHeightContainerMap;
110
111static void insertIntoTrackedRendererMaps(const RenderBlock& container, RenderBox& descendant)
112{
113 if (!percentHeightDescendantsMap) {
114 percentHeightDescendantsMap = new TrackedDescendantsMap;
115 percentHeightContainerMap = new TrackedContainerMap;
116 }
117
118 auto& descendantSet = percentHeightDescendantsMap->ensure(container, [] {
119 return makeUnique<TrackedRendererListHashSet>();
120 }).iterator->value;
121
122 bool added = descendantSet->add(descendant).isNewEntry;
123 if (!added) {
124#if ASSERT_ENABLED0
125 auto it = percentHeightContainerMap->find(descendant);
126 ASSERT(it != percentHeightContainerMap->end())((void)0);
127 ASSERT(it->value.contains(container))((void)0);
128#endif
129 return;
130 }
131
132 auto& containerSet = percentHeightContainerMap->add(descendant, SingleThreadWeakHashSet<const RenderBlock>()).iterator->value;
133 ASSERT(!containerSet.contains(container))((void)0);
134 containerSet.add(container);
135}
136
137static void removeFromTrackedRendererMaps(RenderBox& descendant)
138{
139 if (!percentHeightDescendantsMap)
140 return;
141
142 auto containerSet = percentHeightContainerMap->take(descendant);
143 for (auto& container : containerSet) {
144 // FIXME: Disabling this assert temporarily until we fix the layout
145 // bugs associated with positioned objects not properly cleared from
146 // their ancestor chain before being moved. See webkit bug 93766.
147 // ASSERT(descendant->isDescendantOf(container));
148 auto descendantsMapIterator = percentHeightDescendantsMap->find(container);
149 ASSERT(descendantsMapIterator != percentHeightDescendantsMap->end())((void)0);
150 if (descendantsMapIterator == percentHeightDescendantsMap->end())
151 continue;
152 auto& descendantSet = descendantsMapIterator->value;
153 ASSERT(descendantSet->contains(descendant))((void)0);
154 descendantSet->remove(descendant);
155 if (descendantSet->isEmptyIgnoringNullReferences())
156 percentHeightDescendantsMap->remove(descendantsMapIterator);
157 }
158}
159
160class OutOfFlowDescendantsMap {
161public:
162 void addDescendant(const RenderBlock& containingBlock, RenderBox& outOfFlowDescendant)
163 {
164 // Protect against double insert where a descendant would end up with multiple containing blocks.
165 auto previousContainingBlock = m_containerMap.get(outOfFlowDescendant);
166 if (previousContainingBlock && previousContainingBlock != &containingBlock) {
167 if (auto* descendants = m_descendantsMap.get(*previousContainingBlock))
168 descendants->remove(outOfFlowDescendant);
169 }
170
171 auto& descendants = m_descendantsMap.ensure(containingBlock, [] {
172 return makeUnique<TrackedRendererListHashSet>();
173 }).iterator->value;
174
175 auto isNewEntry = false;
176 if (!is<RenderView>(containingBlock) || descendants->isEmptyIgnoringNullReferences())
177 isNewEntry = descendants->add(outOfFlowDescendant).isNewEntry;
178 else if (outOfFlowDescendant.isFixedPositioned() || isInTopLayerOrBackdrop(outOfFlowDescendant.style(), outOfFlowDescendant.element()))
179 isNewEntry = descendants->appendOrMoveToLast(outOfFlowDescendant).isNewEntry;
180 else {
181 auto ensureLayoutDepentBoxPosition = [&] {
182 // RenderView is a special containing block as it may hold both absolute and fixed positioned containing blocks.
183 // When a fixed positioned box is also a descendant of an absolute positioned box anchored to the RenderView,
184 // we have to make sure that the absolute positioned box is inserted before the fixed box to follow
185 // block layout dependency.
186 for (auto it = descendants->begin(); it != descendants->end(); ++it) {
187 if (it->isFixedPositioned()) {
188 isNewEntry = descendants->insertBefore(it, outOfFlowDescendant).isNewEntry;
189 return;
190 }
191 }
192 isNewEntry = descendants->appendOrMoveToLast(outOfFlowDescendant).isNewEntry;
193 };
194 ensureLayoutDepentBoxPosition();
195 }
196
197 if (!isNewEntry) {
198 ASSERT(m_containerMap.contains(outOfFlowDescendant))((void)0);
199 return;
200 }
201 m_containerMap.set(outOfFlowDescendant, containingBlock);
202 }
203
204 void removeDescendant(const RenderBox& outOfFlowDescendant)
205 {
206 auto containingBlock = m_containerMap.take(outOfFlowDescendant);
207 if (!containingBlock)
208 return;
209
210 auto descendantsIterator = m_descendantsMap.find(*containingBlock.get());
211 ASSERT(descendantsIterator != m_descendantsMap.end())((void)0);
212 if (descendantsIterator == m_descendantsMap.end())
213 return;
214
215 auto& descendants = descendantsIterator->value;
216 ASSERT(descendants->contains(const_cast<RenderBox&>(outOfFlowDescendant)))((void)0);
217
218 descendants->remove(const_cast<RenderBox&>(outOfFlowDescendant));
219 if (descendants->isEmptyIgnoringNullReferences())
220 m_descendantsMap.remove(descendantsIterator);
221 }
222
223 void removeContainingBlock(const RenderBlock& containingBlock)
224 {
225 auto descendants = m_descendantsMap.take(containingBlock);
226 if (!descendants)
227 return;
228
229 for (auto& renderer : *descendants)
230 m_containerMap.remove(renderer);
231 }
232
233 TrackedRendererListHashSet* positionedRenderers(const RenderBlock& containingBlock) const
234 {
235 return m_descendantsMap.get(containingBlock);
236 }
237
238private:
239 using DescendantsMap = SingleThreadWeakHashMap<const RenderBlock, std::unique_ptr<TrackedRendererListHashSet>>;
240 using ContainerMap = SingleThreadWeakHashMap<const RenderBox, SingleThreadWeakPtr<const RenderBlock>>;
241
242 DescendantsMap m_descendantsMap;
243 ContainerMap m_containerMap;
244};
245
246static OutOfFlowDescendantsMap& outOfFlowDescendantsMap()
247{
248 static NeverDestroyed<OutOfFlowDescendantsMap> mapForOutOfFlowDescendants;
249 return mapForOutOfFlowDescendants;
250}
251
252using ContinuationOutlineTableMap = SingleThreadWeakHashMap<RenderBlock, std::unique_ptr<SingleThreadWeakListHashSet<RenderInline>>>;
253
254// Allocated only when some of these fields have non-default values
255
256struct RenderBlockRareData {
257 WTF_MAKE_TZONE_ALLOCATED_INLINE(RenderBlockRareData)public: public: using HeapRef = ::bmalloc::api::HeapRef; using
TZoneDescriptor = ::bmalloc::api::TZoneDescriptor; using TZoneMallocFallback
= ::bmalloc::api::TZoneMallocFallback; using CompactAllocationMode
= ::bmalloc::CompactAllocationMode; static constexpr bool usesTZoneHeap
() { return true; } static constexpr unsigned inheritedSizeClass
() { return ::bmalloc::TZone::sizeClass<RenderBlockRareData
>(); } static constexpr unsigned inheritedAlignment() { return
::bmalloc::TZone::alignment<RenderBlockRareData>(); } __attribute__
((always_inline)) inline void* operator new(size_t, void* p) {
return p; } __attribute__((always_inline)) inline void* operator
new[](size_t, void* p) { return p; } void* operator new[](size_t
size) = delete; void operator delete[](void* p) = delete; __attribute__
((always_inline)) inline void* operator new(size_t, NotNullTag
, void* location) { ((void)0); return location; } void* operator
new(size_t size) { static HeapRef s_heapRef; static const TZoneSpecification
s_heapSpec = { &s_heapRef, TZoneSpecification::encodeSize
<RenderBlockRareData>(), TZoneSpecification::encodeAlignment
<RenderBlockRareData>(), TZoneSpecification::encodeCategory
<RenderBlockRareData>(), ::bmalloc::api::compactAllocationMode
<RenderBlockRareData>(), TZoneSpecification::encodeDescriptor
<RenderBlockRareData>(), }; if (!s_heapRef || size != sizeof
(RenderBlockRareData)) { [[unlikely]] if constexpr (::bmalloc
::api::compactAllocationMode<RenderBlockRareData>() == CompactAllocationMode
::Compact) return ::bmalloc::api::tzoneAllocateCompactSlow(size
, s_heapSpec); return ::bmalloc::api::tzoneAllocateNonCompactSlow
(size, s_heapSpec); } ; if constexpr (::bmalloc::api::compactAllocationMode
<RenderBlockRareData>() == CompactAllocationMode::Compact
) return ::bmalloc::api::tzoneAllocateCompact(s_heapRef); return
::bmalloc::api::tzoneAllocateNonCompact(s_heapRef); } __attribute__
((always_inline)) inline void operator delete(void* p) { ::bmalloc
::api::tzoneFree(p); } __attribute__((always_inline)) inline static
void freeAfterDestruction(void* p) { ::bmalloc::api::tzoneFree
(p); } using WTFIsFastMallocAllocated = int; private: using __makeBtzoneMallocedInlineMacroSemicolonifier
__attribute__((unused)) = int
;
258 WTF_MAKE_NONCOPYABLE(RenderBlockRareData)RenderBlockRareData(const RenderBlockRareData&) = delete;
RenderBlockRareData& operator=(const RenderBlockRareData
&) = delete;
;
259public:
260 RenderBlockRareData()
261 {
262 }
263
264 LayoutUnit m_paginationStrut;
265 LayoutUnit m_pageLogicalOffset;
266 LayoutUnit m_intrinsicBorderForFieldset;
267
268 std::optional<SingleThreadWeakPtr<RenderFragmentedFlow>> m_enclosingFragmentedFlow;
269};
270
271using RenderBlockRareDataMap = SingleThreadWeakHashMap<const RenderBlock, std::unique_ptr<RenderBlockRareData>>;
272static RenderBlockRareDataMap* gRareDataMap;
273
274RenderBlock::RenderBlock(Type type, Element& element, RenderStyle&& style, OptionSet<TypeFlag> baseTypeFlags, TypeSpecificFlags typeSpecificFlags)
275 : RenderBox(type, element, WTF::move(style), baseTypeFlags | TypeFlag::IsRenderBlock, typeSpecificFlags)
276{
277 ASSERT(isRenderBlock())((void)0);
278}
279
280RenderBlock::RenderBlock(Type type, Document& document, RenderStyle&& style, OptionSet<TypeFlag> baseTypeFlags, TypeSpecificFlags typeSpecificFlags)
281 : RenderBox(type, document, WTF::move(style), baseTypeFlags | TypeFlag::IsRenderBlock, typeSpecificFlags)
282{
283 ASSERT(isRenderBlock())((void)0);
284}
285
286RenderBlock::~RenderBlock()
287{
288 // Blocks can be added to gRareDataMap during willBeDestroyed(), so this code can't move there.
289 if (renderBlockHasRareData())
290 gRareDataMap->remove(*this);
291
292 // Do not add any more code here. Add it to willBeDestroyed() instead.
293}
294
295void RenderBlock::styleWillChange(Style::Difference diff, const RenderStyle& newStyle)
296{
297 const RenderStyle* oldStyle = hasInitializedStyle() ? &style() : nullptr;
298 setBlockLevelReplacedOrAtomicInline(newStyle.display().isInlineType());
299 if (oldStyle) {
300 removeOutOfFlowBoxesIfNeededOnStyleChange(*this, *oldStyle, newStyle);
301 if (isLegend() && oldStyle->floating() == Float::None && newStyle.floating() != Float::None)
302 setIsExcludedFromNormalLayout(false);
303 }
304 RenderBox::styleWillChange(diff, newStyle);
305}
306
307bool RenderBlock::scrollbarWidthDidChange(const RenderStyle& oldStyle, const RenderStyle& newStyle, ScrollbarOrientation orientation)
308{
309 return (orientation == ScrollbarOrientation::Vertical ? includeVerticalScrollbarSize() : includeHorizontalScrollbarSize()) && oldStyle.scrollbarWidth() != newStyle.scrollbarWidth();
310}
311
312bool RenderBlock::contentBoxLogicalWidthChanged(const RenderStyle& oldStyle, const RenderStyle& newStyle)
313{
314 if (newStyle.writingMode().isHorizontal()) {
315 return oldStyle.usedBorderLeftWidth() != newStyle.usedBorderLeftWidth()
316 || oldStyle.usedBorderRightWidth() != newStyle.usedBorderRightWidth()
317 || oldStyle.paddingLeft() != newStyle.paddingLeft()
318 || oldStyle.paddingRight() != newStyle.paddingRight()
319 || scrollbarWidthDidChange(oldStyle, newStyle, ScrollbarOrientation::Vertical);
320 }
321
322 return oldStyle.usedBorderTopWidth() != newStyle.usedBorderTopWidth()
323 || oldStyle.usedBorderBottomWidth() != newStyle.usedBorderBottomWidth()
324 || oldStyle.paddingTop() != newStyle.paddingTop()
325 || oldStyle.paddingBottom() != newStyle.paddingBottom()
326 || scrollbarWidthDidChange(oldStyle, newStyle, ScrollbarOrientation::Horizontal);
327}
328
329bool RenderBlock::paddingBoxLogicaHeightChanged(const RenderStyle& oldStyle, const RenderStyle& newStyle)
330{
331 auto scrollbarHeightDidChange = [&] (auto orientation) {
332 return (orientation == ScrollbarOrientation::Vertical ? includeVerticalScrollbarSize() : includeHorizontalScrollbarSize()) && oldStyle.scrollbarWidth() != newStyle.scrollbarWidth();
333 };
334 if (newStyle.writingMode().isHorizontal())
335 return oldStyle.usedBorderTopWidth() != newStyle.usedBorderTopWidth() || oldStyle.usedBorderBottomWidth() != newStyle.usedBorderBottomWidth() || scrollbarHeightDidChange(ScrollbarOrientation::Horizontal);
336 return oldStyle.usedBorderLeftWidth() != newStyle.usedBorderLeftWidth() || oldStyle.usedBorderRightWidth() != newStyle.usedBorderRightWidth() || scrollbarHeightDidChange(ScrollbarOrientation::Vertical);
337}
338
339void RenderBlock::styleDidChange(Style::Difference diff, const RenderStyle* oldStyle)
340{
341 RenderBox::styleDidChange(diff, oldStyle);
342
343 if (oldStyle)
344 adjustFragmentedFlowStateOnContainingBlockChangeIfNeeded(*oldStyle, style());
345
346 propagateStyleToAnonymousChildren(StylePropagationType::BlockAndRubyChildren);
347
348 // It's possible for our border/padding to change, but for the overall logical width of the block to
349 // end up being the same. We keep track of this change so in layoutBlock, we can know to set relayoutChildren=true.
350 auto shouldForceRelayoutChildren = false;
351 if (oldStyle && diff == Style::DifferenceResult::Layout && needsLayout()) {
352 // Out-of-flow boxes anchored to the padding box.
353 shouldForceRelayoutChildren = contentBoxLogicalWidthChanged(*oldStyle, style()) || (outOfFlowBoxes() && paddingBoxLogicaHeightChanged(*oldStyle, style()));
354 }
355 setShouldForceRelayoutChildren(shouldForceRelayoutChildren);
356}
357
358bool RenderBlock::childrenPreventSelfCollapsing() const
359{
360 // Whether or not we collapse is dependent on whether all our normal flow children
361 // are also self-collapsing.
362 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
363 if (child->isFloatingOrOutOfFlowPositioned())
364 continue;
365 if (!child->isSelfCollapsingBlock())
366 return true;
367 }
368 return false;
369}
370
371bool RenderBlock::isSelfCollapsingBlock() const
372{
373 // We are not self-collapsing if we
374 // (a) have a non-zero height according to layout (an optimization to avoid wasting time)
375 // (b) are a table,
376 // (c) have border/padding,
377 // (d) have a min-height
378 // (e) have specified that one of our margins can't collapse using a CSS extension
379
380 if (isOutOfFlowPositioned())
381 return false;
382
383 auto minHeightIsPositive = [&] {
384 return WTF::switchOn(style().logicalMinHeight(),
385 [](const Style::MinimumSize::Fixed& fixedValue) {
386 return fixedValue.isPositive();
387 },
388 [&](const Style::MinimumSize::Percentage& percentageValue) {
389 return percentageValue.isPositive();
390 },
391 [&](const Style::PreferredSize::Calc&) {
392 return true;
393 },
394 [](const auto&) {
395 return false;
396 }
397 );
398 };
399
400 auto heightIsZeroOrAuto = [&] {
401 auto handleNonZeroPercentageOrCalc = [&] {
402 // While in quirks mode there's always a fixed height ancestor to resolve percent value against (ICB),
403 // in standards mode we can only use the containing block.
404 if (document().inQuirksMode())
405 return false;
406 CheckedPtr containingBlock = this->containingBlock();
407 if (!containingBlock) {
408 ASSERT_NOT_REACHED()((void)0);
409 return false;
410 }
411 return is<RenderView>(*containingBlock) || !containingBlock->style().logicalHeight().isFixed();
412 };
413
414 return WTF::switchOn(style().logicalHeight(),
415 [](const Style::PreferredSize::Fixed& fixedValue) {
416 return fixedValue.isZero();
417 },
418 [&](const Style::PreferredSize::Percentage& percentageValue) {
419 if (percentageValue.isZero())
420 return true;
421 return handleNonZeroPercentageOrCalc();
422 },
423 [&](const Style::PreferredSize::Calc&) {
424 return handleNonZeroPercentageOrCalc();
425 },
426 [](const CSS::Keyword::Auto&) {
427 return true;
428 },
429// FIXME(GCC): Can be removed on Aug 9th, 2026 or later.
430#if COMPILER(GCC)(defined WTF_COMPILER_GCC && WTF_COMPILER_GCC) && (__GNUC__4 < 13)
431 [](auto const&) {
432 return false;
433 }
434#else
435 [](CSS::PrimitiveKeyword auto const&) {
436 return false;
437 }
438#endif
439 );
440 };
441
442 if (logicalHeight() > 0 || isRenderTable() || borderAndPaddingLogicalHeight() || minHeightIsPositive())
443 return false;
444
445 if (heightIsZeroOrAuto()) {
446 // If the height is 0 or auto, then whether or not we are a self-collapsing block depends
447 // on whether we have content that is all self-collapsing or not.
448 return !createsNewFormattingContext() && !childrenPreventSelfCollapsing();
449 }
450
451 return false;
452}
453
454void RenderBlock::beginUpdateScrollInfoAfterLayoutTransaction()
455{
456 ++view().frameView().layoutContext().updateScrollInfoAfterLayoutTransaction().nestedCount;
457}
458
459void RenderBlock::endAndCommitUpdateScrollInfoAfterLayoutTransaction()
460{
461 auto* transaction = view().frameView().layoutContext().updateScrollInfoAfterLayoutTransactionIfExists();
462 ASSERT(transaction)((void)0);
463 if (--transaction->nestedCount)
464 return;
465
466 // Calling RenderLayer::updateScrollInfoAfterLayout() may cause its associated block to layout again and
467 // updates its scroll info (i.e. call RenderBlock::updateScrollInfoAfterLayout()). We decrement the nestedCount first
468 // so that all subsequent calls to RenderBlock::updateScrollInfoAfterLayout() are dispatched immediately.
469 // That is, to ensure that such subsequent calls aren't added to |transaction| while we are processing it.
470 auto blocksToUpdate = copyToVector(transaction->blocks);
471 transaction->blocks.clear();
472
473 for (auto block : blocksToUpdate) {
474 if (block->hasControlClip() && block->hasRenderOverflow())
475 block->clearLayoutOverflow();
476 if (block->hasNonVisibleOverflow())
477 block->layer()->updateScrollInfoAfterLayout();
478 }
479}
480
481static inline bool NODELETE[[clang::annotate_type("webkit.nodelete")]] isDelayingUpdateScrollInfoAfterLayout(const RenderBlock& renderer)
482{
483 auto* transaction = renderer.view().frameView().layoutContext().updateScrollInfoAfterLayoutTransactionIfExists();
A function 'isDelayingUpdateScrollInfoAfterLayout' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object
484 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=97937
485 // Workaround for now. We cannot delay the scroll info for overflow
486 // for items with opposite writing directions, as the contents needs
487 // to overflow in that direction
488 return transaction && transaction->nestedCount && !renderer.writingMode().isBlockFlipped();
489};
490
491void RenderBlock::updateScrollInfoAfterLayout()
492{
493 auto hasNonVisibleOverflow = this->hasNonVisibleOverflow();
494
495 if (isDelayingUpdateScrollInfoAfterLayout(*this)) {
496 auto shouldUpdate = hasNonVisibleOverflow || hasControlClip();
497 if (shouldUpdate) {
498 view().frameView().layoutContext().updateScrollInfoAfterLayoutTransactionIfExists()->blocks.add(*this);
499 return;
500 }
501 }
502
503 if (hasNonVisibleOverflow && layer())
504 layer()->updateScrollInfoAfterLayout();
505}
506
507void RenderBlock::layout()
508{
509 StackStats::LayoutCheckPoint layoutCheckPoint;
510
511 // Table cells call layoutBlock directly, so don't add any logic here. Put code into layoutBlock().
512 {
513 auto scope = LayoutScope { *this };
514 layoutBlock(RelayoutChildren::No);
515 }
516
517 // It's safe to check for control clip here, since controls can never be table cells.
518 // If we have a lightweight clip, there can never be any overflow from children.
519 if (hasControlClip() && m_overflow && !isDelayingUpdateScrollInfoAfterLayout(*this))
520 clearLayoutOverflow();
521
522 invalidateBackgroundObscurationStatus();
523}
524
525RenderBlockRareData* RenderBlock::blockRareData() const
526{
527 if (!renderBlockHasRareData())
528 return nullptr;
529 ASSERT(gRareDataMap)((void)0);
530 return gRareDataMap->get(*this);
531}
532
533RenderBlockRareData& RenderBlock::ensureBlockRareData()
534{
535 if (!gRareDataMap)
536 gRareDataMap = new RenderBlockRareDataMap;
537
538 return *gRareDataMap->ensure(*this, [this] {
539 setRenderBlockHasRareData(true);
540 return makeUnique<RenderBlockRareData>();
541 }).iterator->value;
542}
543
544void RenderBlock::preparePaginationBeforeBlockLayout(RelayoutChildren& relayoutChildren)
545{
546 // Fragments changing widths can force us to relayout our children.
547 if (CheckedPtr fragmentedFlow = enclosingFragmentedFlow())
548 fragmentedFlow->logicalWidthChangedInFragmentsForBlock(*this, relayoutChildren);
549}
550
551bool RenderBlock::recomputeLogicalWidth()
552{
553 LayoutUnit oldWidth = logicalWidth();
554
555 updateLogicalWidth();
556
557 bool hasBorderOrPaddingLogicalWidthChanged = this->hasBorderOrPaddingLogicalWidthChanged();
558 setShouldForceRelayoutChildren(false);
559
560 return oldWidth != logicalWidth() || hasBorderOrPaddingLogicalWidthChanged;
561}
562
563void RenderBlock::layoutBlock(RelayoutChildren, LayoutUnit)
564{
565 ASSERT_NOT_REACHED()((void)0);
566}
567
568// Overflow is always relative to the border-box of the element in question.
569// Therefore, if the element has a vertical scrollbar placed on the left, an overflow rect at x=2px would conceptually intersect the scrollbar.
570void RenderBlock::computeInFlowOverflow(LayoutRect contentArea, OptionSet<ComputeOverflowOptions> options)
571{
572 clearOverflow();
573 addOverflowFromInFlowChildren(options);
574
575 if (hasPotentiallyScrollableOverflow() || isRenderView()) {
576 if (!flippedContentBoxRect().contains(contentArea))
577 ensureOverflow();
578 if (hasRenderOverflow()) {
579 m_overflow->addContentOverflow(contentArea);
580 addLayoutOverflow(scrollablePaddingAreaOverflowRect());
581 }
582 }
583
584 // Add visual overflow from box-shadow, border-image-outset and outline.
585 addVisualEffectOverflow();
586
587 // Add visual overflow from theme.
588 addVisualOverflowFromTheme();
589}
590
591void RenderBlock::clearLayoutOverflow()
592{
593 if (!m_overflow)
594 return;
595
596 if (visualOverflowRect() == borderBoxRect()) {
597 // FIXME: Implement complete solution for fragments overflow.
598 clearOverflow();
599 return;
600 }
601
602 m_overflow->setLayoutOverflow(borderBoxRect());
603 m_overflow->setContentArea(flippedContentBoxRect());
604}
605
606void RenderBlock::addOverflowFromOutOfFlowBoxes()
607{
608 TrackedRendererListHashSet* outOfFlowDescendants = outOfFlowBoxes();
609 if (!outOfFlowDescendants)
610 return;
611
612 for (auto& outOfFlowBox : *outOfFlowDescendants) {
613 // Fixed positioned elements don't contribute to layout overflow, since they don't scroll with the content.
614 if (outOfFlowBox.isAbsolutelyPositioned())
615 addOverflowFromContainedBox(outOfFlowBox);
616 }
617}
618
619void RenderBlock::addVisualOverflowFromTheme()
620{
621 if (!style().hasUsedAppearance())
622 return;
623
624 FloatRect inflatedRect = borderBoxRect();
625 theme().adjustRepaintRect(*this, inflatedRect);
626 addVisualOverflow(snappedIntRect(LayoutRect(inflatedRect)));
627
628 if (CheckedPtr fragmentedFlow = enclosingFragmentedFlow())
629 fragmentedFlow->addFragmentsVisualOverflowFromTheme(*this);
630}
631
632void RenderBlock::setLogicalLeftForChild(RenderBox& child, LayoutUnit logicalLeft, ApplyLayoutDeltaMode applyDelta)
633{
634 if (isHorizontalWritingMode()) {
635 if (applyDelta == ApplyLayoutDelta)
636 view().frameView().layoutContext().addLayoutDelta(LayoutSize(child.x() - logicalLeft, 0_lu));
637 child.setX(logicalLeft);
638 } else {
639 if (applyDelta == ApplyLayoutDelta)
640 view().frameView().layoutContext().addLayoutDelta(LayoutSize(0_lu, child.y() - logicalLeft));
641 child.setY(logicalLeft);
642 }
643}
644
645void RenderBlock::setLogicalTopForChild(RenderBox& child, LayoutUnit logicalTop, ApplyLayoutDeltaMode applyDelta)
646{
647 if (isHorizontalWritingMode()) {
648 if (applyDelta == ApplyLayoutDelta)
649 view().frameView().layoutContext().addLayoutDelta(LayoutSize(0_lu, child.y() - logicalTop));
650 child.setY(logicalTop);
651 } else {
652 if (applyDelta == ApplyLayoutDelta)
653 view().frameView().layoutContext().addLayoutDelta(LayoutSize(child.x() - logicalTop, 0_lu));
654 child.setX(logicalTop);
655 }
656}
657
658void RenderBlock::updateBlockChildDirtyBitsBeforeLayout(RelayoutChildren relayoutChildren, RenderBox& child)
659{
660 if (child.isOutOfFlowPositioned())
661 return;
662
663 // FIXME: Technically percentage height objects only need a relayout if their percentage isn't going to be turned into
664 // an auto value. Add a method to determine this, so that we can avoid the relayout.
665 auto childHasRelativeHeight = [&] {
666 auto& style = child.style();
667 return style.height().isPercentOrCalculated() || style.minHeight().isPercentOrCalculated() || style.maxHeight().isPercentOrCalculated();
668 };
669 if (relayoutChildren == RelayoutChildren::Yes || (childHasRelativeHeight() && !isRenderView()))
670 child.setChildNeedsLayout(MarkOnlyThis);
671
672 // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths.
673 if (relayoutChildren == RelayoutChildren::Yes && child.shouldInvalidatePreferredWidths())
674 child.setNeedsPreferredWidthsUpdate(MarkOnlyThis);
675}
676
677void RenderBlock::simplifiedNormalFlowLayout()
678{
679 ASSERT(!childrenInline())((void)0);
680
681 for (auto& box : childrenOfType<RenderBox>(*this)) {
682 if (!box.isOutOfFlowPositioned())
683 box.layoutIfNeeded();
684 }
685}
686
687bool RenderBlock::canPerformSimplifiedLayout() const
688{
689 if (selfNeedsLayout() || normalChildNeedsLayout() || outOfFlowChildNeedsStaticPositionLayout())
690 return false;
691 if (auto wasSkippedDuringLastLayout = wasSkippedDuringLastLayoutDueToContentVisibility(); wasSkippedDuringLastLayout && *wasSkippedDuringLastLayout)
692 return false;
693 if (layoutContext().isSkippedContentRootForLayout(*this) && (outOfFlowChildNeedsLayout() || canContainFixedPositionObjects()))
694 return false;
695 if (isSkippedContentRoot(*this) && firstChild() && firstChild()->wasSkippedDuringLastLayoutDueToContentVisibility())
696 return false;
697 return outOfFlowChildNeedsLayout() || needsSimplifiedNormalFlowLayout();
698}
699
700bool RenderBlock::simplifiedLayout()
701{
702 if (!canPerformSimplifiedLayout())
703 return false;
704
705 LayoutStateMaintainer statePusher(*this, locationOffset(), isTransformed() || hasReflection() || writingMode().isBlockFlipped());
706 if (needsOutOfFlowMovementLayout() && !tryLayoutDoingOutOfFlowMovementOnly())
707 return false;
708
709 // Lay out positioned descendants or objects that just need to recompute overflow.
710 if (needsSimplifiedNormalFlowLayout())
711 simplifiedNormalFlowLayout();
712
713 // Make sure a forced break is applied after the content if we are a flow thread in a simplified layout.
714 // This ensures the size information is correctly computed for the last auto-height fragment receiving content.
715 if (CheckedPtr fragmentedFlow = dynamicDowncast<RenderFragmentedFlow>(*this))
716 fragmentedFlow->applyBreakAfterContent(clientLogicalBottom());
717
718 // Recompute our overflow information.
719 // FIXME: Skip recalculation if we didn't actually relayout our in-flow boxes and don't have cached overflow.
720 auto contentArea = hasRenderOverflow() ? m_overflow->contentArea() : flippedContentBoxRect();
721 computeInFlowOverflow(contentArea, ComputeOverflowOptions::RecomputeFloats);
722
723 // Lay out our positioned objects if our positioned child bit is set.
724 // Also, if an absolute position element inside a relative positioned container moves, and the absolute element has a fixed position
725 // child, neither the fixed element nor its container learn of the movement since outOfFlowChildNeedsLayout() is only marked as far as the
726 // relative positioned container. So if we can have fixed pos objects in our positioned objects list check if any of them
727 // are statically positioned and thus need to move with their absolute ancestors.
728 bool canContainFixedPosObjects = canContainFixedPositionObjects();
729 if (outOfFlowChildNeedsLayout() || canContainFixedPosObjects)
730 layoutOutOfFlowBoxes(RelayoutChildren::No, !outOfFlowChildNeedsLayout() && canContainFixedPosObjects);
731 addOverflowFromOutOfFlowBoxes();
732
733 updateLayerTransform();
734
735 updateScrollInfoAfterLayout();
736
737 clearNeedsLayout();
738 return true;
739}
740
741void RenderBlock::markFixedPositionBoxForLayoutIfNeeded(RenderBox& positionedChild)
742{
743 if (positionedChild.style().position() != PositionType::Fixed)
744 return;
745
746 bool hasStaticBlockPosition = positionedChild.style().hasStaticBlockPosition(isHorizontalWritingMode());
747 bool hasStaticInlinePosition = positionedChild.style().hasStaticInlinePosition(isHorizontalWritingMode());
748 if (!hasStaticBlockPosition && !hasStaticInlinePosition)
749 return;
750
751 auto* parent = positionedChild.parent();
752 while (parent && !is<RenderView>(*parent) && parent->style().position() != PositionType::Absolute)
753 parent = parent->parent();
754 if (!parent || parent->style().position() != PositionType::Absolute)
755 return;
756
757 if (hasStaticInlinePosition) {
758 LogicalExtentComputedValues computedValues;
759 positionedChild.computeLogicalWidth(computedValues);
760 LayoutUnit newLeft = computedValues.position;
761 if (newLeft != positionedChild.logicalLeft())
762 positionedChild.setChildNeedsLayout(MarkOnlyThis);
763 } else if (hasStaticBlockPosition) {
764 auto logicalTop = positionedChild.logicalTop();
765 if (logicalTop != positionedChild.computeLogicalHeight(positionedChild.logicalHeight(), logicalTop).position)
766 positionedChild.setChildNeedsLayout(MarkOnlyThis);
767 }
768}
769
770LayoutUnit RenderBlock::marginIntrinsicLogicalWidthForChild(RenderBox& child) const
771{
772 // A margin has three types: fixed, percentage, and auto (variable).
773 // Auto and percentage margins become 0 when computing min/max width.
774 // Fixed margins can be added in as is.
775 auto& marginLeft = child.style().marginStart(writingMode());
776 auto& marginRight = child.style().marginEnd(writingMode());
777 const auto& zoomFactor = child.style().usedZoomForLength();
778 LayoutUnit margin;
779 if (auto fixedMarginLeft = marginLeft.tryFixed(); fixedMarginLeft && !shouldTrimChildMargin(Style::MarginTrimSide::InlineStart, child))
780 margin += fixedMarginLeft->resolveZoom(zoomFactor);
781 if (auto fixedMarginRight = marginRight.tryFixed(); fixedMarginRight && !shouldTrimChildMargin(Style::MarginTrimSide::InlineEnd, child))
782 margin += fixedMarginRight->resolveZoom(zoomFactor);
783 return margin;
784}
785
786void RenderBlock::layoutOutOfFlowBox(RenderBox& outOfFlowBox, RelayoutChildren relayoutChildren, bool fixedPositionObjectsOnly)
787{
788 ASSERT(outOfFlowBox.isOutOfFlowPositioned())((void)0);
789
790 if (layoutContext().isSkippedContentRootForLayout(*this)) {
791 outOfFlowBox.clearNeedsLayoutForSkippedContent();
792 return;
793 }
794
795 estimateFragmentRangeForBoxChild(outOfFlowBox);
796
797 // A fixed position element with an absolute positioned ancestor has no way of knowing if the latter has changed position. So
798 // if this is a fixed position element, mark it for layout if it has an abspos ancestor and needs to move with that ancestor, i.e.
799 // it has static position.
800 markFixedPositionBoxForLayoutIfNeeded(outOfFlowBox);
801 if (fixedPositionObjectsOnly) {
802 outOfFlowBox.layoutIfNeeded();
803 return;
804 }
805
806 auto& outOfFlowBoxStyle = outOfFlowBox.style();
807 // When a non-positioned block element moves, it may have positioned children that are implicitly positioned relative to the
808 // non-positioned block. Rather than trying to detect all of these movement cases, we just always lay out positioned
809 // objects that are positioned implicitly like this. Such objects are rare, and so in typical DHTML menu usage (where everything is
810 // positioned explicitly) this should not incur a performance penalty.
811 auto needsLayout = [&] {
812 if (relayoutChildren == RelayoutChildren::Yes)
813 return true;
814 if (outOfFlowBox.parent() == this)
815 return false;
816 if (outOfFlowBoxStyle.hasStaticBlockPosition(isHorizontalWritingMode()))
817 return true;
818 if (outOfFlowBoxStyle.hasStaticInlinePosition(isHorizontalWritingMode())) {
819 // FIXME: We could just set the logical left on this out-of-flow box since the box itself does not really need layout (we are just moving an out-of-flow renderer here).
820 return outOfFlowBox.logicalLeft() != PositionedLayoutConstraints { outOfFlowBox, LogicalBoxAxis::Inline }.computedInlineStaticDistance();
821 }
822 return false;
823 };
824 if (needsLayout())
825 outOfFlowBox.setChildNeedsLayout(MarkOnlyThis);
826
827 // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths.
828 if (relayoutChildren == RelayoutChildren::Yes && outOfFlowBox.shouldInvalidatePreferredWidths())
829 outOfFlowBox.setNeedsPreferredWidthsUpdate(MarkOnlyThis);
830
831 outOfFlowBox.markForPaginationRelayoutIfNeeded();
832
833 // We don't have to do a full layout. We just have to update our position. Try that first. If we have shrink-to-fit width
834 // and we hit the available width constraint, the layoutIfNeeded() will catch it and do a full layout.
835 if (outOfFlowBox.needsOutOfFlowMovementLayoutOnly() && outOfFlowBox.tryLayoutDoingOutOfFlowMovementOnly()) {
836 if (Style::AnchorPositionEvaluator::isAnchorPositioned(outOfFlowBoxStyle))
837 Style::AnchorPositionEvaluator::captureScrollSnapshots(outOfFlowBox);
838 outOfFlowBox.clearNeedsLayout();
839 }
840
841 // If we are paginated or in a line grid, compute a vertical position for our object now.
842 // If it's wrong we'll lay out again.
843 LayoutUnit oldLogicalTop;
844 auto* layoutState = view().frameView().layoutContext().layoutState();
845 bool needsBlockDirectionLocationSetBeforeLayout = outOfFlowBox.needsLayout() && layoutState && layoutState->needsBlockDirectionLocationSetBeforeLayout();
846 if (needsBlockDirectionLocationSetBeforeLayout) {
847 if (isHorizontalWritingMode() == outOfFlowBox.isHorizontalWritingMode())
848 outOfFlowBox.updateLogicalHeight();
849 else
850 outOfFlowBox.updateLogicalWidth();
851 oldLogicalTop = logicalTopForChild(outOfFlowBox);
852 }
853
854 outOfFlowBox.layoutIfNeeded();
855
856 auto* parent = outOfFlowBox.parent();
857 bool layoutChanged = false;
858 if (auto* flexibleBox = dynamicDowncast<RenderFlexibleBox>(*parent); flexibleBox && flexibleBox->setStaticPositionForPositionedLayout(outOfFlowBox)) {
859 // The static position of an abspos child of a flexbox depends on its size
860 // (for example, they can be centered). So we may have to reposition the
861 // item after layout.
862 // FIXME: We could probably avoid a layout here and just reposition?
863 layoutChanged = true;
864 }
865
866 // Lay out again if our estimate was wrong.
867 if (layoutChanged || (needsBlockDirectionLocationSetBeforeLayout && logicalTopForChild(outOfFlowBox) != oldLogicalTop)) {
868 outOfFlowBox.setChildNeedsLayout(MarkOnlyThis);
869 outOfFlowBox.layoutIfNeeded();
870 }
871
872 if (updateFragmentRangeForBoxChild(outOfFlowBox)) {
873 outOfFlowBox.setNeedsLayout(MarkOnlyThis);
874 outOfFlowBox.layoutIfNeeded();
875 }
876
877 if (layoutState && layoutState->isPaginated()) {
878 if (CheckedPtr blockFlow = dynamicDowncast<RenderBlockFlow>(*this))
879 blockFlow->adjustSizeContainmentChildForPagination(outOfFlowBox, outOfFlowBox.logicalTop());
880 }
881}
882
883void RenderBlock::layoutOutOfFlowBoxes(RelayoutChildren relayoutChildren, bool fixedPositionObjectsOnly)
884{
885 auto* outOfFlowDescendants = outOfFlowBoxes();
886 if (!outOfFlowDescendants)
887 return;
888
889 // Do not cache outOfFlowDescendants->end() in a local variable, since |outOfFlowDescendants| can be mutated
890 // as it is walked. We always need to fetch the new end() value dynamically.
891 for (auto& descendant : *outOfFlowDescendants)
892 layoutOutOfFlowBox(descendant, relayoutChildren, fixedPositionObjectsOnly);
893}
894
895void RenderBlock::markOutOfFlowBoxesForLayout()
896{
897 auto* outOfFlowDescendants = outOfFlowBoxes();
898 if (!outOfFlowDescendants)
899 return;
900
901 for (auto& descendant : *outOfFlowDescendants)
902 descendant.setChildNeedsLayout();
903}
904
905void RenderBlock::markForPaginationRelayoutIfNeeded()
906{
907 auto* layoutState = view().frameView().layoutContext().layoutState();
908 if (needsLayout() || !layoutState || !layoutState->isPaginated())
909 return;
910
911 if (layoutState->pageLogicalHeightChanged() || (layoutState->pageLogicalHeight() && layoutState->pageLogicalOffset(this, logicalTop()) != pageLogicalOffset()))
912 setChildNeedsLayout(MarkOnlyThis);
913}
914
915void RenderBlock::paintCarets(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
916{
917 if (paintInfo.phase == PaintPhase::Foreground) {
918 paintCaret(paintInfo, paintOffset, CursorCaret);
919 paintCaret(paintInfo, paintOffset, DragCaret);
920 }
921}
922
923void RenderBlock::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
924{
925 auto adjustedPaintOffset = paintOffset + location();
926 PaintPhase phase = paintInfo.phase;
927
928 // FIXME: Could eliminate the isDocumentElementRenderer() check if we fix background painting so that the RenderView paints the root's background.
929 auto visualContentIsClippedOut = [&](LayoutRect paintingRect) {
930 if (isDocumentElementRenderer())
931 return false;
932
933 if (paintInfo.paintBehavior.contains(PaintBehavior::CompositedOverflowScrollContent) && hasLayer() && layer()->usesCompositedScrolling())
934 return false;
935
936 auto overflowBox = visualOverflowRect();
937 flipForWritingMode(overflowBox);
938 overflowBox.moveBy(adjustedPaintOffset);
939 return !overflowBox.intersects(paintingRect);
940 };
941
942 if (visualContentIsClippedOut(paintInfo.rect))
943 return;
944
945 bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset);
946 paintObject(paintInfo, adjustedPaintOffset);
947 if (pushedClip)
948 popContentsClip(paintInfo, phase, adjustedPaintOffset);
949
950 // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with
951 // z-index. We paint after we painted the background/border, so that the scrollbars will
952 // sit above the background/border.
953 if (phase == PaintPhase::BlockBackground || phase == PaintPhase::ChildBlockBackground) {
954 CheckedPtr layer = this->layer();
955 if (hasNonVisibleOverflow() && layer && layer->scrollableArea() && style().usedVisibility() == Visibility::Visible
956 && paintInfo.shouldPaintWithinRoot(*this) && !paintInfo.paintRootBackgroundOnly()) {
957 protect(layer->scrollableArea())->paintOverflowControls(paintInfo.context(), paintInfo.paintBehavior, roundedIntPoint(adjustedPaintOffset), snappedIntRect(paintInfo.rect));
958 }
959 }
960}
961
962PaintInfo RenderBlock::paintInfoForBlockChildren(const PaintInfo& paintInfo) const
963{
964 auto phaseForChildren = [&] {
965 switch (paintInfo.phase) {
966 case PaintPhase::ChildOutlines:
967 return PaintPhase::Outline;
968 case PaintPhase::ChildBlockBackgrounds:
969 return PaintPhase::ChildBlockBackground;
970 default:
971 return paintInfo.phase;
972 }
973 };
974
975 PaintInfo paintInfoForChildren(paintInfo);
976 paintInfoForChildren.phase = phaseForChildren();
977 paintInfoForChildren.updateSubtreePaintRootForChildren(this);
978
979 if (paintInfoForChildren.eventRegionContext())
980 paintInfoForChildren.paintBehavior.add(PaintBehavior::EventRegionIncludeBackground);
981
982 return paintInfoForChildren;
983}
984
985void RenderBlock::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
986{
987 ASSERT(!isSkippedContentRoot(*this))((void)0);
988
989 if (childrenInline())
990 paintInlineChildren(paintInfo, paintOffset);
991 else {
992 PaintInfo paintInfoForChildred = paintInfoForBlockChildren(paintInfo);
993
994 // FIXME: Paint-time pagination is obsolete and is now only used by embedded WebViews inside AppKit
995 // NSViews. Do not add any more code for this.
996 bool usePrintRect = !view().printRect().isEmpty();
997 paintChildren(paintInfo, paintOffset, paintInfoForChildred, usePrintRect);
998 }
999}
1000
1001void RenderBlock::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
1002{
1003 ASSERT(!isSkippedContentRoot(*this))((void)0);
1004
1005 for (auto& child : childrenOfType<RenderBox>(*this)) {
1006 if (!paintChild(child, paintInfo, paintOffset, paintInfoForChild, usePrintRect))
1007 return;
1008 }
1009}
1010
1011bool RenderBlock::paintChild(RenderBox& child, PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect, PaintBlockType paintType)
1012{
1013 ASSERT(!isSkippedContentRoot(*this))((void)0);
1014
1015 if (child.isExcludedAndPlacedInBorder())
1016 return true;
1017
1018 if (child.isSkippedContent()) {
1019 ASSERT(child.isColumnSpanner())((void)0);
1020 return true;
1021 }
1022
1023 // Check for page-break-before: always, and if it's set, break and bail.
1024 bool checkBeforeAlways = !childrenInline() && (usePrintRect && alwaysPageBreak(child.style().breakBefore()));
1025 LayoutUnit absoluteChildY = paintOffset.y() + child.y();
1026 if (checkBeforeAlways
1027 && absoluteChildY > paintInfo.rect.y()
1028 && absoluteChildY < paintInfo.rect.maxY()) {
1029 view().setBestTruncatedAt(absoluteChildY, this, true);
1030 return false;
1031 }
1032
1033 if (!child.isFloating() && child.isBlockLevelReplacedOrAtomicInline() && usePrintRect && child.height() <= view().printRect().height()) {
1034 // Paginate block-level replaced elements.
1035 if (absoluteChildY + child.height() > view().printRect().maxY()) {
1036 if (absoluteChildY < view().truncatedAt())
1037 view().setBestTruncatedAt(absoluteChildY, &child);
1038 // If we were able to truncate, don't paint.
1039 if (absoluteChildY >= view().truncatedAt())
1040 return false;
1041 }
1042 }
1043
1044 LayoutPoint childPoint = flipForWritingModeForChild(child, paintOffset);
1045 if (!child.hasSelfPaintingLayer() && !child.isFloating()) {
1046 if (paintType == PaintAsInlineBlock)
1047 child.paintAsInlineBlock(paintInfoForChild, childPoint);
1048 else
1049 child.paint(paintInfoForChild, childPoint);
1050 }
1051
1052 // Check for page-break-after: always, and if it's set, break and bail.
1053 bool checkAfterAlways = !childrenInline() && (usePrintRect && alwaysPageBreak(child.style().breakAfter()));
1054 if (checkAfterAlways
1055 && (absoluteChildY + child.height()) > paintInfo.rect.y()
1056 && (absoluteChildY + child.height()) < paintInfo.rect.maxY()) {
1057 view().setBestTruncatedAt(absoluteChildY + child.height() + std::max<LayoutUnit>(0, child.collapsedMarginAfter()), this, true);
1058 return false;
1059 }
1060
1061 return true;
1062}
1063
1064void RenderBlock::paintCaret(PaintInfo& paintInfo, const LayoutPoint& paintOffset, CaretType type)
1065{
1066 auto shouldPaintCaret = [&](RenderBlock* caretPainter, bool isContentEditable) {
1067 if (caretPainter != this)
1068 return false;
1069
1070 return isContentEditable || settings().caretBrowsingEnabled();
1071 };
1072
1073 switch (type) {
1074 case CaretType::CursorCaret: {
1075 auto caretPainter = frame().selection().caretRendererWithoutUpdatingLayout();
1076 if (!caretPainter)
1077 return;
1078
1079 bool isContentEditable = frame().selection().selection().hasEditableStyle();
1080
1081 if (shouldPaintCaret(caretPainter, isContentEditable))
1082 frame().selection().paintCaret(paintInfo.context(), paintOffset);
1083 break;
1084 }
1085 case CaretType::DragCaret: {
1086 auto caretPainter = page().dragCaretController().caretRenderer();
1087 if (!caretPainter)
1088 return;
1089
1090 bool isContentEditable = page().dragCaretController().isContentEditable();
1091 if (shouldPaintCaret(caretPainter, isContentEditable))
1092 page().dragCaretController().paintDragCaret(protect(frame()).ptr(), paintInfo.context(), paintOffset);
1093
1094 break;
1095 }
1096 }
1097}
1098
1099void RenderBlock::paintDebugBoxShadowIfApplicable(GraphicsContext& context, const LayoutRect& paintRect) const
1100{
1101 // FIXME: Use a more generic, modern-layout wide setting instead.
1102 if (!settings().legacyLineLayoutVisualCoverageEnabled())
1103 return;
1104
1105 auto* flexBox = dynamicDowncast<RenderFlexibleBox>(this);
1106 if (!flexBox)
1107 return;
1108
1109 constexpr size_t shadowExtent = 3;
1110 GraphicsContextStateSaver stateSaver(context);
1111
1112 auto shadowRect = paintRect;
1113 shadowRect.inflate(shadowExtent);
1114 context.clip(shadowRect);
1115 context.setDropShadow({ { -shadowRect.width(), 0 }, 30, flexBox->hasModernLayout() ? SRGBA<uint8_t> { 0, 180, 230, 200 } : SRGBA<uint8_t> { 200, 100, 100, 200 }, ShadowRadiusMode::Default });
1116 context.clipOut(paintRect);
1117 shadowRect.move(shadowRect.width(), 0);
1118 context.fillRect(shadowRect, Color::black);
1119}
1120
1121void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1122{
1123 PaintPhase paintPhase = paintInfo.phase;
1124 auto shouldPaintContent = !isSkippedContentRoot(*this);
1125
1126 // 1. paint background, borders etc
1127 if ((paintPhase == PaintPhase::BlockBackground || paintPhase == PaintPhase::ChildBlockBackground) && style().usedVisibility() == Visibility::Visible) {
1128 if (hasVisibleBoxDecorations())
1129 paintBoxDecorations(paintInfo, paintOffset);
1130 paintDebugBoxShadowIfApplicable(paintInfo.context(), { paintOffset, size() });
1131 }
1132
1133 // Paint legends just above the border before we scroll or clip.
1134 if (shouldPaintContent && (paintPhase == PaintPhase::BlockBackground || paintPhase == PaintPhase::ChildBlockBackground || paintPhase == PaintPhase::Selection))
1135 paintExcludedChildrenInBorder(paintInfo, paintOffset);
1136
1137 if (paintPhase == PaintPhase::Mask && style().usedVisibility() == Visibility::Visible) {
1138 paintMask(paintInfo, paintOffset);
1139 return;
1140 }
1141
1142 if (paintPhase == PaintPhase::ClippingMask && style().usedVisibility() == Visibility::Visible) {
1143 paintClippingMask(paintInfo, paintOffset);
1144 return;
1145 }
1146
1147 // If just painting the root background, then return.
1148 if (paintInfo.paintRootBackgroundOnly())
1149 return;
1150
1151 if (paintPhase == PaintPhase::Accessibility)
1152 paintInfo.accessibilityRegionContext()->takeBounds(*this, paintOffset);
1153
1154 if (paintPhase == PaintPhase::EventRegion) {
1155 auto borderRect = LayoutRect(paintOffset, size());
1156
1157 Ref document = this->document();
1158 if (paintInfo.paintBehavior.contains(PaintBehavior::EventRegionIncludeBackground) && visibleToHitTesting()) {
1159 auto borderShape = BorderShape::shapeForBorderRect(style(), borderRect);
1160 LOG_WITH_STREAM(EventRegions, stream << "RenderBlock " << *this << " uniting region " << borderShape.deprecatedRoundedRect() << " event listener types " << style().eventListenerRegionTypes())((void)0);
1161 bool overrideUserModifyIsEditable = isRenderTextControl() && protect(downcast<RenderTextControl>(*this).textFormControlElement())->isInnerTextElementEditable();
1162 paintInfo.eventRegionContext()->unite(borderShape.deprecatedPixelSnappedRoundedRect(document->deviceScaleFactor()), *this, style(), overrideUserModifyIsEditable);
1163 }
1164
1165 if (!paintInfo.paintBehavior.contains(PaintBehavior::EventRegionIncludeForeground))
1166 return;
1167
1168 bool needsTraverseDescendants = hasVisualOverflow() || containsFloats() || !paintInfo.eventRegionContext()->contains(enclosingIntRect(borderRect)) || view().needsEventRegionUpdateForNonCompositedFrame();
1169 LOG_WITH_STREAM(EventRegions, stream << "RenderBlock " << *this << " needsTraverseDescendants for event region: hasVisualOverflow: " << hasVisualOverflow() << " containsFloats: "((void)0)
1170 << containsFloats() << " border box is outside current region: " << !paintInfo.eventRegionContext()->contains(enclosingIntRect(borderRect)) << " needsEventRegionUpdateForNonCompositedFrame:" << view().needsEventRegionUpdateForNonCompositedFrame())((void)0);
1171#if ENABLE(TOUCH_ACTION_REGIONS)(defined 1 && 1)
1172 needsTraverseDescendants |= document->mayHaveElementsWithNonAutoTouchAction();
1173 LOG_WITH_STREAM(EventRegions, stream << " may have touch-action elements: " << document->mayHaveElementsWithNonAutoTouchAction())((void)0);
1174#endif
1175#if ENABLE(WHEEL_EVENT_REGIONS)(defined 1 && 1)
1176 needsTraverseDescendants |= document->hasWheelEventHandlers();
1177 LOG_WITH_STREAM(EventRegions, stream << " has wheel event handlers: " << document->hasWheelEventHandlers())((void)0);
1178#endif
1179#if ENABLE(TOUCH_EVENT_REGIONS)(defined 0 && 0)
1180 needsTraverseDescendants |= document->hasTouchEventHandlers();
1181 LOG_WITH_STREAM(EventRegions, stream << " has touch event handlers: " << document->hasTouchEventHandlers())((void)0);
1182#endif
1183
1184#if ENABLE(EDITABLE_REGION)(defined 1 && 1)
1185 // We treat the entire text control as editable to match users' expectation even
1186 // though it's actually the inner text element of the control that is editable.
1187 // So, no need to traverse to find the inner text element in this case.
1188 if (!isRenderTextControl()) {
1189 needsTraverseDescendants |= document->mayHaveEditableElements() && page().shouldBuildEditableRegion();
1190 LOG_WITH_STREAM(EventRegions, stream << " needs editable event region: " << (document->mayHaveEditableElements() && page().shouldBuildEditableRegion()))((void)0);
1191 }
1192#endif
1193
1194#if ENABLE(INTERACTION_REGIONS_IN_EVENT_REGION)(defined ENABLE_INTERACTION_REGIONS_IN_EVENT_REGION &&
ENABLE_INTERACTION_REGIONS_IN_EVENT_REGION)
1195 needsTraverseDescendants |= page().shouldBuildInteractionRegions();
1196#endif
1197
1198 if (!needsTraverseDescendants)
1199 return;
1200 }
1201
1202 // Adjust our painting position if we're inside a scrolled layer (e.g., an overflow:auto div).
1203 LayoutPoint scrolledOffset = paintOffset;
1204 scrolledOffset.moveBy(-scrollPosition());
1205
1206 // Column rules need to account for scrolling and clipping.
1207 // FIXME: Clipping of column rules does not work. We will need a separate paint phase for column rules I suspect in order to get
1208 // clipping correct (since it has to paint as background but is still considered "contents").
1209 if ((paintPhase == PaintPhase::BlockBackground || paintPhase == PaintPhase::ChildBlockBackground) && style().usedVisibility() == Visibility::Visible)
1210 paintColumnRules(paintInfo, scrolledOffset);
1211
1212 // Done with backgrounds, borders and column rules.
1213 if (paintPhase == PaintPhase::BlockBackground)
1214 return;
1215
1216 // 2. paint contents
1217 if (shouldPaintContent) {
1218 if (paintPhase != PaintPhase::SelfOutline)
1219 paintContents(paintInfo, scrolledOffset);
1220 }
1221
1222 // 3. paint selection
1223 if (!protect(document())->printing()) {
1224 // Fill in gaps in selection on lines, between blocks and "empty space" when content is skipped.
1225 paintSelection(paintInfo, scrolledOffset);
1226 }
1227
1228 if (shouldPaintContent) {
1229 // 4. paint floats.
1230 if (paintPhase == PaintPhase::Float || paintPhase == PaintPhase::Selection || paintPhase == PaintPhase::TextClip || paintPhase == PaintPhase::EventRegion || paintPhase == PaintPhase::Accessibility)
1231 paintFloats(paintInfo, scrolledOffset, paintPhase == PaintPhase::Selection || paintPhase == PaintPhase::TextClip || paintPhase == PaintPhase::EventRegion || paintPhase == PaintPhase::Accessibility);
1232 }
1233
1234 // 5. paint outline.
1235 if ((paintPhase == PaintPhase::Outline || paintPhase == PaintPhase::SelfOutline) && hasOutline() && style().usedVisibility() == Visibility::Visible) {
1236 // Don't paint focus ring for anonymous block continuation because the
1237 // inline element having outline-style:auto paints the whole focus ring.
1238 if (style().outlineStyle() != OutlineStyle::Auto || !isContinuation())
1239 paintOutline(paintInfo, LayoutRect(paintOffset, size()));
1240 }
1241
1242 // 6. paint continuation outlines.
1243 if ((paintPhase == PaintPhase::Outline || paintPhase == PaintPhase::ChildOutlines)) {
1244 RenderInline* inlineCont = inlineContinuation();
1245 if (inlineCont && inlineCont->hasOutline() && inlineCont->style().usedVisibility() == Visibility::Visible) {
1246 RenderInline* inlineRenderer = downcast<RenderInline>(inlineCont->element()->renderer());
1247 RenderBlock* containingBlock = this->containingBlock();
1248
1249 bool inlineEnclosedInSelfPaintingLayer = false;
1250 for (RenderBoxModelObject* box = inlineRenderer; box != containingBlock; box = &box->parent()->enclosingBoxModelObject()) {
1251 if (box->hasSelfPaintingLayer()) {
1252 inlineEnclosedInSelfPaintingLayer = true;
1253 break;
1254 }
1255 }
1256
1257 // Do not add continuations for outline painting by our containing block if we are a relative positioned
1258 // anonymous block (i.e. have our own layer), paint them straightaway instead. This is because a block depends on renderers in its continuation table being
1259 // in the same layer.
1260 if (!inlineEnclosedInSelfPaintingLayer && !hasLayer())
1261 containingBlock->addContinuationWithOutline(inlineRenderer);
1262 else if (!InlineIterator::lineLeftmostInlineBoxFor(*inlineRenderer) || (!inlineEnclosedInSelfPaintingLayer && hasLayer())) {
1263 auto outlineOffset = paintOffset - locationOffset() + inlineRenderer->containingBlock()->location();
1264 OutlinePainter { paintInfo }.paintOutline(*inlineRenderer, outlineOffset);
1265 }
1266 }
1267 paintContinuationOutlines(paintInfo, paintOffset);
1268 }
1269
1270 // 7. paint caret.
1271 // If the caret's node's render object's containing block is this block, and the paint action is PaintPhase::Foreground,
1272 // then paint the caret.
1273 if (shouldPaintContent)
1274 paintCarets(paintInfo, paintOffset);
1275}
1276
1277static ContinuationOutlineTableMap* continuationOutlineTable()
1278{
1279 static NeverDestroyed<ContinuationOutlineTableMap> table;
1280 return &table.get();
1281}
1282
1283void RenderBlock::addContinuationWithOutline(RenderInline* flow)
1284{
1285 // We can't make this work if the inline is in a layer. We'll just rely on the broken
1286 // way of painting.
1287 ASSERT(!flow->layer() && !flow->isContinuation())((void)0);
1288
1289 auto* table = continuationOutlineTable();
1290 auto* continuations = table->get(*this);
1291 if (!continuations) {
1292 continuations = new SingleThreadWeakListHashSet<RenderInline>;
1293 table->set(*this, std::unique_ptr<SingleThreadWeakListHashSet<RenderInline>>(continuations));
1294 }
1295
1296 continuations->add(*flow);
1297}
1298
1299bool RenderBlock::establishesIndependentFormattingContextIgnoringDisplayType(const RenderStyle& style) const
1300{
1301 RefPtr element = this->element();
1302 if (!element) {
1303 ASSERT(isAnonymous())((void)0);
1304 return false;
1305 }
1306
1307 auto isBlockBoxWithPotentiallyScrollableOverflow = [&] {
1308 return style.display().isBlockType()
1309 && style.display().doesGenerateBlockContainer()
1310 && hasNonVisibleOverflow()
1311 && style.overflowX() != Overflow::Clip
1312 && style.overflowX() != Overflow::Visible;
1313 };
1314
1315 return style.floating() != Float::None
1316 || style.hasOutOfFlowPosition()
1317 || isBlockBoxWithPotentiallyScrollableOverflow()
1318 || style.usedContain().contains(Style::ContainValue::Layout)
1319 || style.containerType() != ContainerType::Normal
1320 || Style::ContainmentChecker { style, *element }.shouldApplyPaintContainment()
1321 || (style.display().isBlockType() && !style.blockStepSize().isNone());
1322}
1323
1324bool RenderBlock::establishesIndependentFormattingContext() const
1325{
1326 auto& style = this->style();
1327 if (establishesIndependentFormattingContextIgnoringDisplayType(style))
1328 return true;
1329
1330 if (isGridItem()) {
1331 // Grid items establish a new independent formatting context, unless they're a subgrid
1332 // https://drafts.csswg.org/css-grid-2/#grid-item-display
1333 if (!style.gridTemplateColumns().subgrid && !style.gridTemplateRows().subgrid)
1334 return true;
1335 // Masonry makes grid items not subgrids.
1336 if (CheckedPtr parentGridBox = dynamicDowncast<RenderGrid>(parent()))
1337 return parentGridBox->isMasonry();
1338 }
1339
1340 return false;
1341}
1342
1343bool RenderBlock::createsNewFormattingContext() const
1344{
1345 // Writing-mode changes establish an independent block formatting context
1346 // if the box is a block-container.
1347 // https://drafts.csswg.org/css-writing-modes/#block-flow
1348 if (isWritingModeRoot() && isBlockContainer())
1349 return true;
1350 auto& style = this->style();
1351 if (isBlockContainer() && !style.alignContent().isNormal())
1352 return true;
1353 return isNonReplacedAtomicInlineLevelBox()
1354 || style.display().isFlexibleBoxIncludingDeprecatedOrGridFormattingContextBox()
1355 || isFlexItemIncludingDeprecated()
1356 || isRenderTable()
1357 || isRenderTableCell()
1358 || isRenderTableCaption()
1359 || isFieldset()
1360 || isDocumentElementRenderer()
1361 || isRenderFragmentedFlow()
1362 || isRenderOrLegacyRenderSVGForeignObject()
1363 || style.specifiesColumns()
1364 || style.columnSpan() == ColumnSpan::All
1365 || style.display() == Style::DisplayType::BlockFlowRoot
1366 || establishesIndependentFormattingContext();
1367}
1368
1369#if ASSERT_ENABLED0
1370bool RenderBlock::paintsContinuationOutline(const RenderInline& renderer)
1371{
1372 if (auto* continuations = continuationOutlineTable()->get(*this))
1373 return continuations->contains(renderer);
1374 return false;
1375}
1376#endif
1377
1378void RenderBlock::paintContinuationOutlines(PaintInfo& info, const LayoutPoint& paintOffset)
1379{
1380 auto* table = continuationOutlineTable();
1381 auto continuations = table->take(*this);
1382 if (!continuations)
1383 return;
1384
1385 LayoutPoint accumulatedPaintOffset = paintOffset;
1386 // Paint each continuation outline.
1387 OutlinePainter outlinePainter { info };
1388 for (auto& renderInline : *continuations) {
1389 // Need to add in the coordinates of the intervening blocks.
1390 auto* block = renderInline.containingBlock();
1391 for ( ; block && block != this; block = block->containingBlock())
1392 accumulatedPaintOffset.moveBy(block->location());
1393 ASSERT(block)((void)0);
1394 outlinePainter.paintOutline(renderInline, accumulatedPaintOffset);
1395 }
1396}
1397
1398bool RenderBlock::shouldPaintSelectionGaps() const
1399{
1400 return selectionState() != HighlightState::None && style().usedVisibility() == Visibility::Visible && isSelectionRoot();
1401}
1402
1403bool RenderBlock::isSelectionRoot() const
1404{
1405 if (isPseudoElement())
1406 return false;
1407 ASSERT(element() || isAnonymous())((void)0);
1408
1409 // FIXME: Eventually tables should have to learn how to fill gaps between cells, at least in simple non-spanning cases.
1410 if (isRenderTable())
1411 return false;
1412
1413 if (isBody() || isDocumentElementRenderer() || hasNonVisibleOverflow()
1414 || isPositioned() || isFloating()
1415 || isRenderTableCell() || isNonReplacedAtomicInlineLevelBox()
1416 || isTransformed() || hasReflection() || hasMask() || isWritingModeRoot()
1417 || isRenderFragmentedFlow() || style().columnSpan() == ColumnSpan::All
1418 || isFlexItemIncludingDeprecated() || isGridItem())
1419 return true;
1420
1421 if (view().selection().start()) {
1422 RefPtr startElement = view().selection().start()->node();
1423 if (startElement && startElement->rootEditableElement() == element())
1424 return true;
1425 }
1426
1427 return false;
1428}
1429
1430GapRects RenderBlock::selectionGapRectsForRepaint(const RenderLayerModelObject* repaintContainer)
1431{
1432 ASSERT(!needsLayout())((void)0);
1433
1434 if (!shouldPaintSelectionGaps())
1435 return { };
1436
1437 FloatPoint containerPoint = localToContainerPoint(FloatPoint(), repaintContainer, UseTransforms);
1438 LayoutPoint offsetFromRepaintContainer(containerPoint - toFloatSize(scrollPosition()));
1439
1440#if ENABLE(TEXT_SELECTION)(defined 0 && 0)
1441 ASSERT(isSelectionRoot())((void)0);
1442#endif
1443 LogicalSelectionOffsetCaches cache(*this);
1444 LayoutUnit lastTop;
1445 LayoutUnit lastLeft = logicalLeftSelectionOffset(*this, lastTop, cache);
1446 LayoutUnit lastRight = logicalRightSelectionOffset(*this, lastTop, cache);
1447
1448 return selectionGaps(*this, offsetFromRepaintContainer, IntSize(), lastTop, lastLeft, lastRight, cache);
1449}
1450
1451void RenderBlock::paintSelection(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1452{
1453#if ENABLE(TEXT_SELECTION)(defined 0 && 0)
1454 if (shouldPaintSelectionGaps() && paintInfo.phase == PaintPhase::Foreground) {
1455 ASSERT(isSelectionRoot())((void)0);
1456 LogicalSelectionOffsetCaches cache(*this);
1457 LayoutUnit lastTop;
1458 LayoutUnit lastLeft = logicalLeftSelectionOffset(*this, lastTop, cache);
1459 LayoutUnit lastRight = logicalRightSelectionOffset(*this, lastTop, cache);
1460 GraphicsContextStateSaver stateSaver(paintInfo.context());
1461
1462 LayoutRect gapRectsBounds = selectionGaps(*this, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, cache, &paintInfo);
1463 if (!gapRectsBounds.isEmpty()) {
1464 if (CheckedPtr layer = enclosingLayer()) {
1465 gapRectsBounds.moveBy(-paintOffset);
1466 if (!hasLayer()) {
1467 LayoutRect localBounds(gapRectsBounds);
1468 flipForWritingMode(localBounds);
1469 gapRectsBounds = localToContainerQuad(FloatRect(localBounds), &layer->renderer()).enclosingBoundingBox();
1470 if (layer->renderer().isRenderBox())
1471 gapRectsBounds.moveBy(layer->renderBox()->scrollPosition());
1472 }
1473 layer->addBlockSelectionGapsBounds(gapRectsBounds);
1474 }
1475 }
1476 }
1477#else
1478 UNUSED_PARAM(paintInfo)(void)paintInfo;
1479 UNUSED_PARAM(paintOffset)(void)paintOffset;
1480#endif
1481}
1482
1483static void clipOutOutOfFlowBoxes(const PaintInfo* paintInfo, const LayoutPoint& offset, TrackedRendererListHashSet* outOfFlowBoxes)
1484{
1485 if (!outOfFlowBoxes)
1486 return;
1487
1488 for (auto& renderer : *outOfFlowBoxes)
1489 paintInfo->context().clipOut(IntRect(offset.x() + renderer.x(), offset.y() + renderer.y(), renderer.width(), renderer.height()));
1490}
1491
1492LayoutUnit blockDirectionOffset(RenderBlock& rootBlock, const LayoutSize& offsetFromRootBlock)
1493{
1494 return rootBlock.isHorizontalWritingMode() ? offsetFromRootBlock.height() : offsetFromRootBlock.width();
1495}
1496
1497LayoutUnit inlineDirectionOffset(RenderBlock& rootBlock, const LayoutSize& offsetFromRootBlock)
1498{
1499 return rootBlock.isHorizontalWritingMode() ? offsetFromRootBlock.width() : offsetFromRootBlock.height();
1500}
1501
1502LayoutRect RenderBlock::logicalRectToPhysicalRect(const LayoutPoint& rootBlockPhysicalPosition, const LayoutRect& logicalRect)
1503{
1504 LayoutRect result;
1505 if (isHorizontalWritingMode())
1506 result = logicalRect;
1507 else
1508 result = LayoutRect(logicalRect.y(), logicalRect.x(), logicalRect.height(), logicalRect.width());
1509 flipForWritingMode(result);
1510 result.moveBy(rootBlockPhysicalPosition);
1511 return result;
1512}
1513
1514GapRects RenderBlock::selectionGaps(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
1515 LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo)
1516{
1517 // IMPORTANT: Callers of this method that intend for painting to happen need to do a save/restore.
1518 // Clip out floating and positioned objects when painting selection gaps.
1519 if (paintInfo) {
1520 // Note that we don't clip out overflow for positioned objects. We just stick to the border box.
1521 LayoutRect flippedBlockRect(offsetFromRootBlock.width(), offsetFromRootBlock.height(), width(), height());
1522 rootBlock.flipForWritingMode(flippedBlockRect);
1523 flippedBlockRect.moveBy(rootBlockPhysicalPosition);
1524 clipOutOutOfFlowBoxes(paintInfo, flippedBlockRect.location(), outOfFlowBoxes());
1525 if (isBody() || isDocumentElementRenderer()) { // The <body> must make sure to examine its containingBlock's positioned objects.
1526 for (RenderBlock* cb = containingBlock(); cb && !is<RenderView>(*cb); cb = cb->containingBlock())
1527 clipOutOutOfFlowBoxes(paintInfo, LayoutPoint(cb->x(), cb->y()), cb->outOfFlowBoxes()); // FIXME: Not right for flipped writing modes.
1528 }
1529 clipOutFloatingBoxes(rootBlock, paintInfo, rootBlockPhysicalPosition, offsetFromRootBlock);
1530 }
1531
1532 // FIXME: overflow: auto/scroll fragments need more math here, since painting in the border box is different from painting in the padding box (one is scrolled, the other is fixed).
1533 if (!is<RenderBlockFlow>(*this)) {
1534 // FIXME: Make multi-column selection gap filling work someday.
1535 return { };
1536 }
1537
1538 if (isFlexItem() || isGridItem() || isDeprecatedFlexItem()) {
1539 // FIXME: Adding a selection gap to these blocks would produce correct (visual) result only if we could also paint selection gaps between them,
1540 // as we do for blocks in a BFC. Returning an empty gap rect here means we only paint the selection over the content,
1541 // as opposed to expand it all the way to the end of the container.
1542 return { };
1543 }
1544
1545 if (isTransformed() || style().columnSpan() == ColumnSpan::All || isRenderFragmentedFlow()) {
1546 // FIXME: We should learn how to gap fill multiple columns and transforms eventually.
1547 lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalHeight();
1548 lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight(), cache);
1549 lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight(), cache);
1550 return { };
1551 }
1552
1553 GapRects result;
1554 if (childrenInline())
1555 result = inlineSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, cache, paintInfo);
1556 else
1557 result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, cache, paintInfo);
1558
1559 // Fill the vertical gap all the way to the bottom of our block if the selection extends past our block.
1560 if (&rootBlock == this && (selectionState() != HighlightState::Both && selectionState() != HighlightState::End)) {
1561 result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock,
1562 lastLogicalTop, lastLogicalLeft, lastLogicalRight, logicalHeight(), cache, paintInfo));
1563 }
1564
1565 return result;
1566}
1567
1568GapRects NODELETE[[clang::annotate_type("webkit.nodelete")]] RenderBlock::inlineSelectionGaps(RenderBlock&, const LayoutPoint&, const LayoutSize&, LayoutUnit&, LayoutUnit&, LayoutUnit&, const LogicalSelectionOffsetCaches&, const PaintInfo*)
1569{
1570 ASSERT_NOT_REACHED()((void)0);
1571 return GapRects();
1572}
1573
1574GapRects RenderBlock::blockSelectionGaps(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
1575 LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo)
1576{
1577 ASSERT(!isSkippedContent())((void)0);
1578
1579 if (isSkippedContentRoot(*this))
1580 return { };
1581
1582 // Jump right to the first block child that contains some selected objects.
1583 RenderBox* curr;
1584 for (curr = firstChildBox(); curr && curr->selectionState() == HighlightState::None; curr = curr->nextSiblingBox()) { }
1585
1586 if (!curr)
1587 return { };
1588
1589 LogicalSelectionOffsetCaches childCache(*this, cache);
1590
1591 GapRects result;
1592 for (bool sawSelectionEnd = false; curr && !sawSelectionEnd; curr = curr->nextSiblingBox()) {
1593 HighlightState childState = curr->selectionState();
1594 if (childState == HighlightState::Both || childState == HighlightState::End)
1595 sawSelectionEnd = true;
1596
1597 if (curr->isFloatingOrOutOfFlowPositioned())
1598 continue; // We must be a normal flow object in order to even be considered.
1599
1600 if (curr->isInFlowPositioned() && curr->hasLayer()) {
1601 // If the relposition offset is anything other than 0, then treat this just like an absolute positioned element.
1602 // Just disregard it completely.
1603 LayoutSize relOffset = curr->layer()->offsetForInFlowPosition();
1604 if (relOffset.width() || relOffset.height())
1605 continue;
1606 }
1607
1608 // FIXME: Eventually we won't special-case table and other layout roots like this.
1609 auto propagatesSelectionToChildren = isAnyOf<RenderTable, RenderFlexibleBox, RenderDeprecatedFlexibleBox, RenderGrid>(*curr);
1610 auto paintsOwnSelection = curr->shouldPaintSelectionGaps() || propagatesSelectionToChildren;
1611 bool fillBlockGaps = paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != HighlightState::None);
1612 if (fillBlockGaps) {
1613 // We need to fill the vertical gap above this object.
1614 if (childState == HighlightState::End || childState == HighlightState::Inside) {
1615 // Fill the gap above the object.
1616 result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock,
1617 lastLogicalTop, lastLogicalLeft, lastLogicalRight, curr->logicalTop(), cache, paintInfo));
1618 }
1619
1620 // Only fill side gaps for objects that paint their own selection if we know for sure the selection is going to extend all the way *past*
1621 // our object. We know this if the selection did not end inside our object.
1622 if (paintsOwnSelection && (childState == HighlightState::Start || sawSelectionEnd))
1623 childState = HighlightState::None;
1624
1625 // Fill side gaps on this object based off its state.
1626 bool leftGap, rightGap;
1627 getSelectionGapInfo(childState, leftGap, rightGap);
1628
1629 if (leftGap)
1630 result.uniteLeft(logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalLeft(), curr->logicalTop(), curr->logicalHeight(), cache, paintInfo));
1631 if (rightGap)
1632 result.uniteRight(logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalRight(), curr->logicalTop(), curr->logicalHeight(), cache, paintInfo));
1633
1634 // Update lastLogicalTop to be just underneath the object. lastLogicalLeft and lastLogicalRight extend as far as
1635 // they can without bumping into floating or positioned objects. Ideally they will go right up
1636 // to the border of the root selection block.
1637 lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + curr->logicalBottom();
1638 lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, curr->logicalBottom(), cache);
1639 lastLogicalRight = logicalRightSelectionOffset(rootBlock, curr->logicalBottom(), cache);
1640 } else if (childState != HighlightState::None) {
1641 if (auto* renderBlock = dynamicDowncast<RenderBlock>(*curr)) {
1642 // We must be a block that has some selected object inside it, so recur.
1643 result.unite(renderBlock->selectionGaps(rootBlock, rootBlockPhysicalPosition, LayoutSize(offsetFromRootBlock.width() + curr->x(), offsetFromRootBlock.height() + curr->y()),
1644 lastLogicalTop, lastLogicalLeft, lastLogicalRight, childCache, paintInfo));
1645 }
1646 }
1647 }
1648 return result;
1649}
1650
1651LayoutRect RenderBlock::blockSelectionGap(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
1652 LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo)
1653{
1654 LayoutUnit logicalTop = lastLogicalTop;
1655 LayoutUnit logicalHeight = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalBottom - logicalTop;
1656 if (logicalHeight <= 0)
1657 return LayoutRect();
1658
1659 // Get the selection offsets for the bottom of the gap
1660 LayoutUnit logicalLeft = std::max(lastLogicalLeft, logicalLeftSelectionOffset(rootBlock, logicalBottom, cache));
1661 LayoutUnit logicalRight = std::min(lastLogicalRight, logicalRightSelectionOffset(rootBlock, logicalBottom, cache));
1662 LayoutUnit logicalWidth = logicalRight - logicalLeft;
1663 if (logicalWidth <= 0)
1664 return LayoutRect();
1665
1666 LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(logicalLeft, logicalTop, logicalWidth, logicalHeight));
1667 if (paintInfo)
1668 paintInfo->context().fillRect(snapRectToDevicePixels(gapRect, protect(document())->deviceScaleFactor()), selectionBackgroundColor());
1669 return gapRect;
1670}
1671
1672LayoutRect RenderBlock::logicalLeftSelectionGap(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, RenderElement* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo)
1673{
1674 LayoutUnit rootBlockLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalTop;
1675 LayoutUnit rootBlockLogicalLeft = std::max(logicalLeftSelectionOffset(rootBlock, logicalTop, cache), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight, cache));
1676 LayoutUnit rootBlockLogicalRight = std::min(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + logicalLeft,
1677 std::min(logicalRightSelectionOffset(rootBlock, logicalTop, cache), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight, cache)));
1678 LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft;
1679 if (rootBlockLogicalWidth <= 0)
1680 return LayoutRect();
1681
1682 LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight));
1683 if (paintInfo)
1684 paintInfo->context().fillRect(snapRectToDevicePixels(gapRect, protect(document())->deviceScaleFactor()), selObj->selectionBackgroundColor());
1685 return gapRect;
1686}
1687
1688LayoutRect RenderBlock::logicalRightSelectionGap(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, RenderElement* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo)
1689{
1690 LayoutUnit rootBlockLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalTop;
1691 LayoutUnit rootBlockLogicalLeft = std::max(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + logicalRight,
1692 std::max(logicalLeftSelectionOffset(rootBlock, logicalTop, cache), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight, cache)));
1693 LayoutUnit rootBlockLogicalRight = std::min(logicalRightSelectionOffset(rootBlock, logicalTop, cache), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight, cache));
1694 LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft;
1695 if (rootBlockLogicalWidth <= 0)
1696 return LayoutRect();
1697
1698 LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight));
1699 if (paintInfo)
1700 paintInfo->context().fillRect(snapRectToDevicePixels(gapRect, protect(document())->deviceScaleFactor()), selObj->selectionBackgroundColor());
1701 return gapRect;
1702}
1703
1704void RenderBlock::getSelectionGapInfo(HighlightState state, bool& leftGap, bool& rightGap)
1705{
1706 bool ltr = writingMode().isLogicalLeftInlineStart();
1707 leftGap = (state == RenderObject::HighlightState::Inside) || (state == RenderObject::HighlightState::End && ltr) || (state == RenderObject::HighlightState::Start && !ltr);
1708 rightGap = (state == RenderObject::HighlightState::Inside) || (state == RenderObject::HighlightState::Start && ltr) || (state == RenderObject::HighlightState::End && !ltr);
1709}
1710
1711LayoutUnit RenderBlock::logicalLeftSelectionOffset(RenderBlock& rootBlock, LayoutUnit position, const LogicalSelectionOffsetCaches& cache)
1712{
1713 LayoutUnit logicalLeft = logicalLeftOffsetForLine(position);
1714 if (logicalLeft == logicalLeftOffsetForContent()) {
1715 if (&rootBlock != this) // The border can potentially be further extended by our containingBlock().
1716 return cache.containingBlockInfo(*this).logicalLeftSelectionOffset(rootBlock, position + logicalTop());
1717 return logicalLeft;
1718 }
1719
1720 RenderBlock* cb = this;
1721 const LogicalSelectionOffsetCaches* currentCache = &cache;
1722 while (cb != &rootBlock) {
1723 logicalLeft += cb->logicalLeft();
1724
1725 ASSERT(currentCache)((void)0);
1726 auto info = currentCache->containingBlockInfo(*cb);
1727 cb = info.block();
1728 if (!cb)
1729 break;
1730 currentCache = info.cache();
1731 }
1732 return logicalLeft;
1733}
1734
1735LayoutUnit RenderBlock::logicalRightSelectionOffset(RenderBlock& rootBlock, LayoutUnit position, const LogicalSelectionOffsetCaches& cache)
1736{
1737 LayoutUnit logicalRight = logicalRightOffsetForLine(position);
1738 if (logicalRight == logicalRightOffsetForContent()) {
1739 if (&rootBlock != this) // The border can potentially be further extended by our containingBlock().
1740 return cache.containingBlockInfo(*this).logicalRightSelectionOffset(rootBlock, position + logicalTop());
1741 return logicalRight;
1742 }
1743
1744 RenderBlock* cb = this;
1745 const LogicalSelectionOffsetCaches* currentCache = &cache;
1746 while (cb != &rootBlock) {
1747 logicalRight += cb->logicalLeft();
1748
1749 ASSERT(currentCache)((void)0);
1750 auto info = currentCache->containingBlockInfo(*cb);
1751 cb = info.block();
1752 if (!cb)
1753 break;
1754 currentCache = info.cache();
1755 }
1756 return logicalRight;
1757}
1758
1759TrackedRendererListHashSet* RenderBlock::outOfFlowBoxes() const
1760{
1761 return outOfFlowDescendantsMap().positionedRenderers(*this);
1762}
1763
1764void RenderBlock::addOutOfFlowBox(RenderBox& outOfFlowBox)
1765{
1766 ASSERT(outOfFlowBox.isOutOfFlowPositioned())((void)0);
1767 ASSERT(!isAnonymousBlock())((void)0);
1768
1769 outOfFlowBox.clearGridAreaContentSize();
1770
1771 if (outOfFlowBox.isRenderFragmentedFlow())
1772 return;
1773 // FIXME: Find out if we can do this as part of outOfFlowBox.setChildNeedsLayout(MarkOnlyThis)
1774 if (outOfFlowBox.needsLayout()) {
1775 // We should turn this bit on only while in layout.
1776 ASSERT(outOfFlowChildNeedsLayout() || view().frameView().layoutContext().isInLayout())((void)0);
1777 setOutOfFlowChildNeedsLayoutBit(true);
1778 }
1779 outOfFlowDescendantsMap().addDescendant(*this, outOfFlowBox);
1780}
1781
1782void RenderBlock::removeOutOfFlowBox(const RenderBox& rendererToRemove)
1783{
1784 outOfFlowDescendantsMap().removeDescendant(rendererToRemove);
1785}
1786
1787static inline void markRendererAndParentForLayout(RenderBox& renderer)
1788{
1789 renderer.setChildNeedsLayout(MarkOnlyThis);
1790 if (renderer.shouldInvalidatePreferredWidths())
1791 renderer.setNeedsPreferredWidthsUpdate(MarkOnlyThis);
1792 auto* parentBlock = RenderObject::containingBlockForPositionType(PositionType::Static, renderer);
1793 if (!parentBlock) {
1794 ASSERT_NOT_REACHED()((void)0);
1795 return;
1796 }
1797 // Parent has to be mark for layout to run static positioning on the out-of-flow content.
1798 parentBlock->setChildNeedsLayout();
1799}
1800
1801void RenderBlock::removeOutOfFlowBoxes(const RenderBlock* newContainingBlockCandidate, ContainingBlockState containingBlockState)
1802{
1803 auto* outOfFlowDescendants = outOfFlowBoxes();
1804 if (!outOfFlowDescendants)
1805 return;
1806
1807 Vector<CheckedRef<RenderBox>, 16> renderersToRemove;
1808 if (!newContainingBlockCandidate) {
1809 // We don't form containing block for these boxes anymore (either through style change or internal render tree shuffle)
1810 for (auto& renderer : *outOfFlowDescendants) {
1811 renderersToRemove.append(renderer);
1812
1813 markRendererAndParentForLayout(renderer);
1814 auto markNewContainingBlockForLayout = [&] {
1815 auto isAbsolutePositioned = renderer.isAbsolutelyPositioned();
1816 // During style change we can't tell which ancestor is going to be the final containing block, so let's just mark the new candidate dirty.
1817 auto* newContainingBlock = containingBlock();
1818 for (; newContainingBlock && (isAbsolutePositioned ? !newContainingBlock->canContainAbsolutelyPositionedObjects() : newContainingBlock->canContainFixedPositionObjects()); newContainingBlock = newContainingBlock->containingBlock()) { }
1819 if (newContainingBlock)
1820 newContainingBlock->setNeedsLayout();
1821 };
1822 markNewContainingBlockForLayout();
1823 }
1824 } else if (containingBlockState == ContainingBlockState::NewContainingBlock) {
1825 // Some of the positioned boxes are getting transferred over to newContainingBlockCandidate.
1826 for (auto& renderer : *outOfFlowDescendants) {
1827 if (!renderer.isDescendantOf(newContainingBlockCandidate))
1828 continue;
1829 renderersToRemove.append(renderer);
1830 markRendererAndParentForLayout(renderer);
1831 }
1832 } else
1833 ASSERT_NOT_REACHED()((void)0);
1834
1835 for (auto& renderer : renderersToRemove)
1836 removeOutOfFlowBox(renderer);
1837}
1838
1839void RenderBlock::addPercentHeightDescendant(RenderBox& descendant)
1840{
1841 insertIntoTrackedRendererMaps(*this, descendant);
1842}
1843
1844void RenderBlock::removePercentHeightDescendant(RenderBox& descendant)
1845{
1846 // We query the map directly, rather than looking at style's
1847 // logicalHeight()/logicalMinHeight()/logicalMaxHeight() since those
1848 // can change with writing mode/directional changes.
1849 removeFromTrackedRendererMaps(descendant);
1850}
1851
1852TrackedRendererListHashSet* RenderBlock::percentHeightDescendants() const
1853{
1854 return percentHeightDescendantsMap ? percentHeightDescendantsMap->get(*this) : nullptr;
1855}
1856
1857bool RenderBlock::hasPercentHeightContainerMap()
1858{
1859 return percentHeightContainerMap;
1860}
1861
1862void RenderBlock::clearPercentHeightDescendantsFrom(RenderBox& parent)
1863{
1864 if (!percentHeightContainerMap)
1865 return;
1866
1867 for (RenderObject* child = parent.firstChild(); child; child = child->nextInPreOrder(&parent)) {
1868 if (CheckedPtr box = dynamicDowncast<RenderBox>(*child))
1869 removeFromTrackedRendererMaps(*box);
1870 }
1871}
1872
1873bool RenderBlock::isContainingBlockAncestorFor(RenderObject& renderer) const
1874{
1875 for (const auto* ancestor = renderer.containingBlock(); ancestor; ancestor = ancestor->containingBlock()) {
1876 if (ancestor == this)
1877 return true;
1878 }
1879 return false;
1880}
1881
1882LayoutUnit RenderBlock::textIndentOffset() const
1883{
1884 LayoutUnit cw;
1885 if (style().textIndent().length.isPercentOrCalculated())
1886 cw = contentBoxLogicalWidth();
1887 return Style::evaluate<LayoutUnit>(style().textIndent().length, cw, style().usedZoomForLength());
1888}
1889
1890LayoutUnit RenderBlock::logicalLeftOffsetForContent() const
1891{
1892 LayoutUnit logicalLeftOffset = writingMode().isHorizontal() ? borderLeft() + paddingLeft() : borderTop() + paddingTop();
1893 if (isHorizontalWritingMode() && shouldPlaceVerticalScrollbarOnLeft())
1894 logicalLeftOffset += verticalScrollbarWidth();
1895 return logicalLeftOffset;
1896}
1897
1898LayoutUnit RenderBlock::logicalRightOffsetForContent() const
1899{
1900 LayoutUnit logicalRightOffset = writingMode().isHorizontal() ? borderLeft() + paddingLeft() : borderTop() + paddingTop();
1901 if (isHorizontalWritingMode() && shouldPlaceVerticalScrollbarOnLeft())
1902 logicalRightOffset += verticalScrollbarWidth();
1903 logicalRightOffset += contentBoxLogicalWidth();
1904 return logicalRightOffset;
1905}
1906
1907LayoutUnit RenderBlock::adjustLogicalLeftOffsetForLine(LayoutUnit offsetFromFloats) const
1908{
1909 LayoutUnit left = offsetFromFloats;
1910
1911 if (style().lineAlign() == LineAlign::None)
1912 return left;
1913
1914 // Push in our left offset so that it is aligned with the character grid.
1915 auto* layoutState = view().frameView().layoutContext().layoutState();
1916 if (!layoutState)
1917 return left;
1918
1919 RenderBlock* lineGrid = layoutState->lineGrid();
1920 if (!lineGrid || lineGrid->writingMode().computedWritingMode() != writingMode().computedWritingMode())
1921 return left;
1922
1923 // FIXME: Should letter-spacing apply? This is complicated since it doesn't apply at the edge?
1924 float maxCharWidth = lineGrid->style().fontCascade().primaryFont().maxCharWidth();
1925 if (!maxCharWidth)
1926 return left;
1927
1928 LayoutUnit lineGridOffset = lineGrid->isHorizontalWritingMode() ? layoutState->lineGridOffset().width(): layoutState->lineGridOffset().height();
1929 LayoutUnit layoutOffset = lineGrid->isHorizontalWritingMode() ? layoutState->layoutOffset().width() : layoutState->layoutOffset().height();
1930
1931 // Push in to the nearest character width (truncated so that we pixel snap left).
1932 // FIXME: Should be patched when subpixel layout lands, since this calculation doesn't have to pixel snap
1933 // any more (https://bugs.webkit.org/show_bug.cgi?id=79946).
1934 // FIXME: This is wrong for RTL (https://bugs.webkit.org/show_bug.cgi?id=79945).
1935 // FIXME: This doesn't work with columns or fragments (https://bugs.webkit.org/show_bug.cgi?id=79942).
1936 // FIXME: This doesn't work when the inline position of the object isn't set ahead of time.
1937 // FIXME: Dynamic changes to the font or to the inline position need to result in a deep relayout.
1938 // (https://bugs.webkit.org/show_bug.cgi?id=79944)
1939 float remainder = fmodf(maxCharWidth - fmodf(left + layoutOffset - lineGridOffset, maxCharWidth), maxCharWidth);
1940 left += remainder;
1941 return left;
1942}
1943
1944LayoutUnit RenderBlock::adjustLogicalRightOffsetForLine(LayoutUnit offsetFromFloats) const
1945{
1946 LayoutUnit right = offsetFromFloats;
1947
1948 if (style().lineAlign() == LineAlign::None)
1949 return right;
1950
1951 // Push in our right offset so that it is aligned with the character grid.
1952 auto* layoutState = view().frameView().layoutContext().layoutState();
1953 if (!layoutState)
1954 return right;
1955
1956 RenderBlock* lineGrid = layoutState->lineGrid();
1957 if (!lineGrid || lineGrid->writingMode().computedWritingMode() != writingMode().computedWritingMode())
1958 return right;
1959
1960 // FIXME: Should letter-spacing apply? This is complicated since it doesn't apply at the edge?
1961 float maxCharWidth = lineGrid->style().fontCascade().primaryFont().maxCharWidth();
1962 if (!maxCharWidth)
1963 return right;
1964
1965 LayoutUnit lineGridOffset = lineGrid->isHorizontalWritingMode() ? layoutState->lineGridOffset().width(): layoutState->lineGridOffset().height();
1966 LayoutUnit layoutOffset = lineGrid->isHorizontalWritingMode() ? layoutState->layoutOffset().width() : layoutState->layoutOffset().height();
1967
1968 // Push in to the nearest character width (truncated so that we pixel snap right).
1969 // FIXME: Should be patched when subpixel layout lands, since this calculation doesn't have to pixel snap
1970 // any more (https://bugs.webkit.org/show_bug.cgi?id=79946).
1971 // FIXME: This is wrong for RTL (https://bugs.webkit.org/show_bug.cgi?id=79945).
1972 // FIXME: This doesn't work with columns or fragments (https://bugs.webkit.org/show_bug.cgi?id=79942).
1973 // FIXME: This doesn't work when the inline position of the object isn't set ahead of time.
1974 // FIXME: Dynamic changes to the font or to the inline position need to result in a deep relayout.
1975 // (https://bugs.webkit.org/show_bug.cgi?id=79944)
1976 float remainder = fmodf(fmodf(right + layoutOffset - lineGridOffset, maxCharWidth), maxCharWidth);
1977 right -= ceilf(remainder);
1978 return right;
1979}
1980
1981bool RenderBlock::isPointInOverflowControl(HitTestResult& result, const LayoutPoint& locationInContainer, const LayoutPoint& accumulatedOffset)
1982{
1983 if (!scrollsOverflow())
1984 return false;
1985 if (CheckedPtr scrollableArea = layer() ? layer()->scrollableArea() : nullptr)
1986 return scrollableArea->hitTestOverflowControls(result, roundedIntPoint(locationInContainer - toLayoutSize(accumulatedOffset)));
1987 return false;
1988}
1989
1990Node* RenderBlock::nodeForHitTest() const
1991{
1992 if (auto type = style().pseudoElementType()) {
1993 switch (*type) {
1994 case PseudoElementType::Backdrop:
1995 // If we're a ::backdrop pseudo-element, we should hit-test to the element that generated it.
1996 // This matches the behavior that other browsers have.
1997 for (auto& element : document().topLayerElements()) {
1998 if (!element->renderer())
1999 continue;
2000 ASSERT(element->renderer()->pseudoElementRenderer(PseudoElementType::Backdrop))((void)0);
2001 if (element->renderer()->pseudoElementRenderer(PseudoElementType::Backdrop) == this)
2002 return element.ptr();
2003 }
2004 ASSERT_NOT_REACHED()((void)0);
2005 break;
2006
2007 case PseudoElementType::ViewTransition:
2008 case PseudoElementType::ViewTransitionGroup:
2009 case PseudoElementType::ViewTransitionImagePair:
2010 // The view transition pseudo-elements should hit-test to their originating element (the document element).
2011 return document().documentElement();
2012
2013 default:
2014 break;
2015 }
2016 }
2017
2018 // If we are in the margins of block elements that are part of a
2019 // continuation we're actually still inside the enclosing element
2020 // that was split. Use the appropriate inner node.
2021 if (auto* continuation = this->continuation())
2022 return continuation->element();
2023 return element();
2024}
2025
2026bool RenderBlock::hitTestChildren(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& adjustedLocation, HitTestAction hitTestAction)
2027{
2028 ASSERT(!isSkippedContentRoot(*this))((void)0);
2029
2030 // Hit test descendants first.
2031 const LayoutSize localOffset = toLayoutSize(adjustedLocation);
2032 const LayoutSize scrolledOffset(localOffset - toLayoutSize(scrollPosition()));
2033
2034 if (hitTestAction == HitTestFloat && hitTestFloats(request, result, locationInContainer, toLayoutPoint(scrolledOffset)))
2035 return true;
2036 if (hitTestContents(request, result, locationInContainer, toLayoutPoint(scrolledOffset), hitTestAction)) {
2037 updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset));
2038 return true;
2039 }
2040 return false;
2041}
2042
2043bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
2044{
2045 const LayoutPoint adjustedLocation(accumulatedOffset + location());
2046 const LayoutSize localOffset = toLayoutSize(adjustedLocation);
2047
2048 // Check if we need to do anything at all.
2049 if (!hitTestVisualOverflow(locationInContainer, accumulatedOffset))
2050 return false;
2051
2052 if ((hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground)
2053 && visibleToHitTesting(request) && isPointInOverflowControl(result, locationInContainer.point(), adjustedLocation)) {
2054 updateHitTestResult(result, locationInContainer.point() - localOffset);
2055 // FIXME: isPointInOverflowControl() doesn't handle rect-based tests yet.
2056 if (result.addNodeToListBasedTestResult(protect(nodeForHitTest()).get(), request, locationInContainer) == HitTestProgress::Stop)
2057 return true;
2058 }
2059
2060 if (!hitTestClipPath(locationInContainer, accumulatedOffset))
2061 return false;
2062
2063 auto shouldHittestContent = !isSkippedContentRoot(*this);
2064 if (shouldHittestContent) {
2065 // If we have clipping, then we can't have any spillout.
2066 bool useClip = (hasControlClip() || hasNonVisibleOverflow());
2067 bool checkChildren = !useClip || (hasControlClip() ? locationInContainer.intersects(controlClipRect(adjustedLocation)) : locationInContainer.intersects(overflowClipRect(adjustedLocation, OverlayScrollbarSizeRelevancy::IncludeOverlayScrollbarSize)));
2068 if (checkChildren && hitTestChildren(request, result, locationInContainer, adjustedLocation, hitTestAction))
2069 return true;
2070
2071 if (!checkChildren && hitTestExcludedChildrenInBorder(request, result, locationInContainer, adjustedLocation, hitTestAction))
2072 return true;
2073 }
2074
2075 if (!hitTestBorderRadius(locationInContainer, accumulatedOffset))
2076 return false;
2077
2078 // Now hit test our background
2079 if (hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) {
2080 LayoutRect boundsRect(adjustedLocation, size());
2081 if (visibleToHitTesting(request) && locationInContainer.intersects(boundsRect)) {
2082 updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset));
2083 if (result.addNodeToListBasedTestResult(protect(nodeForHitTest()).get(), request, locationInContainer, boundsRect) == HitTestProgress::Stop)
2084 return true;
2085 }
2086 }
2087
2088 return false;
2089}
2090
2091bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
2092{
2093 if (childrenInline() && !isRenderTable()) {
2094 if (style().isSkippedRootOrSkippedContent())
2095 return false;
2096 return hitTestInlineChildren(request, result, locationInContainer, accumulatedOffset, hitTestAction);
2097 }
2098
2099 // Hit test our children.
2100 HitTestAction childHitTest = hitTestAction;
2101 if (hitTestAction == HitTestChildBlockBackgrounds)
2102 childHitTest = HitTestChildBlockBackground;
2103 for (auto* child = lastChildBox(); child; child = child->previousSiblingBox()) {
2104 LayoutPoint childPoint = flipForWritingModeForChild(*child, accumulatedOffset);
2105 if (!child->hasSelfPaintingLayer() && !child->isFloating() && child->nodeAtPoint(request, result, locationInContainer, childPoint, childHitTest))
2106 return true;
2107 }
2108
2109 return false;
2110}
2111
2112static inline bool isEditingBoundary(RenderElement* ancestor, RenderBox& child)
2113{
2114 ASSERT(!ancestor || ancestor->nonPseudoElement())((void)0);
2115 ASSERT(child.nonPseudoElement())((void)0);
2116 return !ancestor || !ancestor->parent() || (ancestor->hasLayer() && ancestor->parent()->isRenderView())
2117 || protect(ancestor->nonPseudoElement())->hasEditableStyle() == protect(child.nonPseudoElement())->hasEditableStyle();
2118}
2119
2120// FIXME: This function should go on RenderObject as an instance method. Then
2121// all cases in which positionForPoint recurs could call this instead to
2122// prevent crossing editable boundaries. This would require many tests.
2123PositionWithAffinity positionForPointRespectingEditingBoundaries(RenderBlock& parent, RenderBox& child, const LayoutPoint& pointInParentCoordinates, HitTestSource source)
2124{
2125 LayoutPoint childLocation = child.location();
2126 if (child.isInFlowPositioned())
2127 childLocation += child.offsetForInFlowPosition();
2128
2129 // FIXME: This is wrong if the child's writing-mode is different from the parent's.
2130 LayoutPoint pointInChildCoordinates(toLayoutPoint(pointInParentCoordinates - childLocation));
2131
2132 // If this is an anonymous renderer, we just recur normally
2133 RefPtr childElement= child.nonPseudoElement();
2134 if (!childElement)
2135 return child.positionForPoint(pointInChildCoordinates, source, nullptr);
2136
2137 // Otherwise, first make sure that the editability of the parent and child agree.
2138 // If they don't agree, then we return a visible position just before or after the child
2139 RenderElement* ancestor = &parent;
2140 while (ancestor && !ancestor->nonPseudoElement())
2141 ancestor = ancestor->parent();
2142
2143 // If we can't find an ancestor to check editability on, or editability is unchanged, we recur like normal
2144 if (isEditingBoundary(ancestor, child))
2145 return child.positionForPoint(pointInChildCoordinates, source, nullptr);
2146
2147 // Otherwise return before or after the child, depending on if the click was to the logical left or logical right of the child
2148 LayoutUnit childMiddle = parent.logicalWidthForChild(child) / 2;
2149 LayoutUnit logicalLeft = parent.isHorizontalWritingMode() ? pointInChildCoordinates.x() : pointInChildCoordinates.y();
2150 if (logicalLeft < childMiddle)
2151 return ancestor->createPositionWithAffinity(childElement->computeNodeIndex(), Affinity::Downstream);
2152 return ancestor->createPositionWithAffinity(childElement->computeNodeIndex() + 1, Affinity::Upstream);
2153}
2154
2155PositionWithAffinity RenderBlock::positionForPointWithInlineChildren(const LayoutPoint&, HitTestSource)
2156{
2157 ASSERT_NOT_REACHED()((void)0);
2158 return PositionWithAffinity();
2159}
2160
2161static inline bool NODELETE[[clang::annotate_type("webkit.nodelete")]] isChildHitTestCandidate(const RenderBox& box, HitTestSource source)
2162{
2163 auto visibility = source == HitTestSource::Script ? box.style().visibility() : box.style().usedVisibility();
2164 return box.height() && visibility == Visibility::Visible && !box.isOutOfFlowPositioned() && !box.isRenderFragmentedFlow();
2165}
2166
2167// Valid candidates in a FragmentedFlow must be rendered by the fragment.
2168static inline bool isChildHitTestCandidate(const RenderBox& box, const RenderFragmentContainer* fragment, const LayoutPoint& point, HitTestSource source)
2169{
2170 if (!isChildHitTestCandidate(box, source))
2171 return false;
2172 if (!fragment)
2173 return true;
2174 auto& block = [&]() -> const RenderBlock& {
2175 if (auto* block = dynamicDowncast<RenderBlock>(box))
2176 return *block;
2177 return *box.containingBlock();
2178 }();
2179 return block.fragmentAtBlockOffset(point.y()) == fragment;
2180}
2181
2182PositionWithAffinity RenderBlock::positionForPoint(const LayoutPoint& point, HitTestSource source, const RenderFragmentContainer* fragment)
2183{
2184 if (isRenderTable())
2185 return RenderBox::positionForPoint(point, source, fragment);
2186
2187 if (isBlockLevelReplacedOrAtomicInline()) {
2188 // FIXME: This seems wrong when the object's writing-mode doesn't match the line's writing-mode.
2189 LayoutUnit pointLogicalLeft = isHorizontalWritingMode() ? point.x() : point.y();
2190 LayoutUnit pointLogicalTop = isHorizontalWritingMode() ? point.y() : point.x();
2191
2192 if (pointLogicalLeft < 0)
2193 return createPositionWithAffinity(caretMinOffset(), Affinity::Downstream);
2194 if (pointLogicalLeft >= logicalWidth())
2195 return createPositionWithAffinity(caretMaxOffset(), Affinity::Downstream);
2196 if (pointLogicalTop < 0)
2197 return createPositionWithAffinity(caretMinOffset(), Affinity::Downstream);
2198 if (pointLogicalTop >= logicalHeight())
2199 return createPositionWithAffinity(caretMaxOffset(), Affinity::Downstream);
2200 }
2201 if (isFlexibleBoxIncludingDeprecated() || isRenderGrid())
2202 return RenderBox::positionForPoint(point, source, fragment);
2203
2204 LayoutPoint pointInContents = point;
2205 offsetForContents(pointInContents);
2206 LayoutPoint pointInLogicalContents(pointInContents);
2207 if (!isHorizontalWritingMode())
2208 pointInLogicalContents = pointInLogicalContents.transposedPoint();
2209
2210 if (childrenInline())
2211 return positionForPointWithInlineChildren(pointInLogicalContents, source);
2212
2213 RenderBox* lastCandidateBox = lastChildBox();
2214
2215 if (!fragment)
2216 fragment = fragmentAtBlockOffset(pointInLogicalContents.y());
2217
2218 while (lastCandidateBox && !isChildHitTestCandidate(*lastCandidateBox, fragment, pointInLogicalContents, source))
2219 lastCandidateBox = lastCandidateBox->previousSiblingBox();
2220
2221 bool blocksAreFlipped = writingMode().isBlockFlipped();
2222 if (lastCandidateBox) {
2223 if (pointInLogicalContents.y() > logicalTopForChild(*lastCandidateBox)
2224 || (!blocksAreFlipped && pointInLogicalContents.y() == logicalTopForChild(*lastCandidateBox)))
2225 return positionForPointRespectingEditingBoundaries(*this, *lastCandidateBox, pointInContents, source);
2226
2227 for (auto& childBox : childrenOfType<RenderBox>(*this)) {
2228 if (!isChildHitTestCandidate(childBox, fragment, pointInLogicalContents, source))
2229 continue;
2230 auto childLogicalBottom = logicalTopForChild(childBox) + logicalHeightForChild(childBox);
2231 if (auto* blockFlow = dynamicDowncast<RenderBlockFlow>(childBox))
2232 childLogicalBottom = std::max(childLogicalBottom, blockFlow->lowestFloatLogicalBottom());
2233 // We hit child if our click is above the bottom of its padding box (like IE6/7 and FF3).
2234 if (pointInLogicalContents.y() < childLogicalBottom || (blocksAreFlipped && pointInLogicalContents.y() == childLogicalBottom))
2235 return positionForPointRespectingEditingBoundaries(*this, childBox, pointInContents, source);
2236 }
2237 }
2238
2239 // We only get here if there are no hit test candidate children below the click.
2240 return RenderBox::positionForPoint(point, source, fragment);
2241}
2242
2243void RenderBlock::offsetForContents(LayoutPoint& offset) const
2244{
2245 offset = flipForWritingMode(offset);
2246 offset += toLayoutSize(scrollPosition());
2247 offset = flipForWritingMode(offset);
2248}
2249
2250bool RenderBlock::willStretchItem(const RenderBox& item, LogicalBoxAxis containingAxis, StretchingMode mode) const
2251{
2252 UNUSED_PARAM(item)(void)item;
2253 UNUSED_PARAM(containingAxis)(void)containingAxis;
2254 UNUSED_PARAM(mode)(void)mode;
2255 return false;
2256}
2257
2258void RenderBlock::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
2259{
2260 ASSERT(!childrenInline())((void)0);
2261 if (shouldApplySizeOrInlineSizeContainment()) {
2262 if (auto width = explicitIntrinsicInnerLogicalWidth()) {
2263 minLogicalWidth = width.value();
2264 maxLogicalWidth = width.value();
2265 }
2266 } else if (!shouldApplyInlineSizeContainment())
2267 computeBlockPreferredLogicalWidths(minLogicalWidth, maxLogicalWidth);
2268
2269 maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth);
2270
2271 int scrollbarWidth = intrinsicScrollbarLogicalWidthIncludingGutter();
2272 maxLogicalWidth += scrollbarWidth;
2273 minLogicalWidth += scrollbarWidth;
2274}
2275
2276void RenderBlock::computePreferredLogicalWidths()
2277{
2278 ASSERT(needsPreferredLogicalWidthsUpdate())((void)0);
2279
2280 m_minPreferredLogicalWidth = 0;
2281 m_maxPreferredLogicalWidth = 0;
2282
2283 auto& styleToUse = style();
2284 auto logicalWidth = overridingLogicalWidthForFlexBasisComputation().value_or(styleToUse.logicalWidth());
2285 if (auto fixedLogicalWidth = logicalWidth.tryFixed(); !isRenderTableCell() && fixedLogicalWidth && fixedLogicalWidth->isPositiveOrZero() && !(isDeprecatedFlexItem() && !static_cast<int>(fixedLogicalWidth->resolveZoom(style().usedZoomForLength())))) {
2286 m_minPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(*fixedLogicalWidth);
2287 m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth;
2288 } else if (logicalWidth.isMaxContent()) {
2289 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
2290 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
2291 } else if (shouldComputeLogicalWidthFromAspectRatio()) {
2292 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = (computeLogicalWidthFromAspectRatio() - borderAndPaddingLogicalWidth());
2293 m_minPreferredLogicalWidth = std::max(0_lu, m_minPreferredLogicalWidth);
2294 m_maxPreferredLogicalWidth = std::max(0_lu, m_maxPreferredLogicalWidth);
2295 } else
2296 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
2297
2298 RenderBox::computePreferredLogicalWidths(styleToUse.logicalMinWidth(), styleToUse.logicalMaxWidth(), borderAndPaddingLogicalWidth());
2299
2300 clearNeedsPreferredWidthsUpdate();
2301}
2302
2303void RenderBlock::computeBlockPreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
2304{
2305 ASSERT(!shouldApplyInlineSizeContainment())((void)0);
2306 auto* containingBlock = this->containingBlock();
2307 if (!containingBlock) {
2308 ASSERT_NOT_REACHED()((void)0);
2309 return;
2310 }
2311
2312 LayoutUnit childMinWidth;
2313 LayoutUnit childMaxWidth;
2314 if (computePreferredWidthsForExcludedChildren(childMinWidth, childMaxWidth)) {
2315 minLogicalWidth = std::max(childMinWidth, minLogicalWidth);
2316 maxLogicalWidth = std::max(childMaxWidth, maxLogicalWidth);
2317 }
2318
2319 LayoutUnit floatLeftWidth;
2320 LayoutUnit floatRightWidth;
2321 auto nowrap = style().textWrapMode() == TextWrapMode::NoWrap && style().whiteSpaceCollapse() == WhiteSpaceCollapse::Collapse;
2322 for (auto& childBox : childrenOfType<RenderBox>(*this)) {
2323 // Positioned children don't affect the min/max width. Legends in fieldsets are skipped here
2324 // since they compute outside of any one layout system. Other children excluded from
2325 // normal layout are only used with block flows, so it's ok to calculate them here.
2326 if (childBox.isOutOfFlowPositioned() || childBox.isExcludedAndPlacedInBorder())
2327 continue;
2328
2329 auto& childStyle = childBox.style();
2330 // Either the box itself of its content avoids floats.
2331 auto childAvoidsFloats = childBox.avoidsFloats() || (childBox.isAnonymousBlock() && childBox.childrenInline());
2332 if (childBox.isFloating() || childAvoidsFloats) {
2333 LayoutUnit floatTotalWidth = floatLeftWidth + floatRightWidth;
2334 auto childUsedClear = RenderStyle::usedClear(childBox);
2335 if (childUsedClear == UsedClear::Left || childUsedClear == UsedClear::Both) {
2336 maxLogicalWidth = std::max(floatTotalWidth, maxLogicalWidth);
2337 floatLeftWidth = 0.f;
2338 }
2339 if (childUsedClear == UsedClear::Right || childUsedClear == UsedClear::Both) {
2340 maxLogicalWidth = std::max(floatTotalWidth, maxLogicalWidth);
2341 floatRightWidth = 0.f;
2342 }
2343 }
2344
2345 // A margin basically has three types: fixed, percentage, and auto (variable).
2346 // Auto and percentage margins simply become 0 when computing min/max width.
2347 // Fixed margins can be added in as is.
2348 LayoutUnit marginStart;
2349 LayoutUnit marginEnd;
2350 const auto& childZoomFactor = childStyle.usedZoomForLength();
2351 if (auto fixedMarginStart = childStyle.marginStart(writingMode()).tryFixed())
2352 marginStart += fixedMarginStart->resolveZoom(childZoomFactor);
2353 if (auto fixedMarginEnd = childStyle.marginEnd(writingMode()).tryFixed())
2354 marginEnd += fixedMarginEnd->resolveZoom(childZoomFactor);
2355 auto margin = marginStart + marginEnd;
2356
2357 LayoutUnit childMinPreferredLogicalWidth;
2358 LayoutUnit childMaxPreferredLogicalWidth;
2359 computeChildPreferredLogicalWidths(const_cast<RenderBox&>(childBox), childMinPreferredLogicalWidth, childMaxPreferredLogicalWidth);
2360
2361 auto logicalWidth = childMinPreferredLogicalWidth + margin;
2362 minLogicalWidth = std::max(logicalWidth, minLogicalWidth);
2363
2364 // IE ignores tables for calculation of nowrap. Makes some sense.
2365 if (nowrap && !childBox.isRenderTable())
2366 maxLogicalWidth = std::max(logicalWidth, maxLogicalWidth);
2367
2368 logicalWidth = childMaxPreferredLogicalWidth + margin;
2369
2370 if (!childBox.isFloating()) {
2371 if (childAvoidsFloats) {
2372 // Determine a left and right max value based off whether or not the floats can fit in the
2373 // margins of the object. For negative margins, we will attempt to overlap the float if the negative margin
2374 // is smaller than the float width.
2375 bool ltr = containingBlock->writingMode().isLogicalLeftInlineStart();
2376 LayoutUnit marginLogicalLeft = ltr ? marginStart : marginEnd;
2377 LayoutUnit marginLogicalRight = ltr ? marginEnd : marginStart;
2378 LayoutUnit maxLeft = marginLogicalLeft > 0 ? std::max(floatLeftWidth, marginLogicalLeft) : floatLeftWidth + marginLogicalLeft;
2379 LayoutUnit maxRight = marginLogicalRight > 0 ? std::max(floatRightWidth, marginLogicalRight) : floatRightWidth + marginLogicalRight;
2380 logicalWidth = childMaxPreferredLogicalWidth + maxLeft + maxRight;
2381 logicalWidth = std::max(logicalWidth, floatLeftWidth + floatRightWidth);
2382 } else
2383 maxLogicalWidth = std::max(floatLeftWidth + floatRightWidth, maxLogicalWidth);
2384 floatLeftWidth = 0.f;
2385 floatRightWidth = 0.f;
2386 }
2387
2388 if (childBox.isFloating()) {
2389 if (RenderStyle::usedFloat(childBox) == UsedFloat::Left)
2390 floatLeftWidth += logicalWidth;
2391 else
2392 floatRightWidth += logicalWidth;
2393 } else
2394 maxLogicalWidth = std::max(logicalWidth, maxLogicalWidth);
2395 }
2396
2397 // Always make sure these values are non-negative.
2398 minLogicalWidth = std::max(0_lu, minLogicalWidth);
2399 maxLogicalWidth = std::max(0_lu, maxLogicalWidth);
2400
2401 maxLogicalWidth = std::max(floatLeftWidth + floatRightWidth, maxLogicalWidth);
2402}
2403
2404void RenderBlock::computeChildIntrinsicLogicalWidths(RenderBox& child, LayoutUnit& minPreferredLogicalWidth, LayoutUnit& maxPreferredLogicalWidth) const
2405{
2406 minPreferredLogicalWidth = child.minPreferredLogicalWidth();
2407 maxPreferredLogicalWidth = child.maxPreferredLogicalWidth();
2408}
2409
2410void RenderBlock::computeChildPreferredLogicalWidths(RenderBox& childBox, LayoutUnit& minPreferredLogicalWidth, LayoutUnit& maxPreferredLogicalWidth) const
2411{
2412 if (childBox.isHorizontalWritingMode() != isHorizontalWritingMode()) {
2413 // If the child is an orthogonal flow, child's height determines the width,
2414 // but the height is not available until layout.
2415 // http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-shrink-to-fit
2416 if (!childBox.needsLayout()) {
2417 minPreferredLogicalWidth = childBox.logicalHeight();
2418 maxPreferredLogicalWidth = childBox.logicalHeight();
2419 return;
2420 }
2421 auto& childBoxStyle = childBox.style();
2422 if (auto fixedChildBoxStyleLogicalWidth = childBoxStyle.logicalWidth().tryFixed(); childBox.shouldComputeLogicalHeightFromAspectRatio() && fixedChildBoxStyleLogicalWidth) {
2423 auto aspectRatioSize = blockSizeFromAspectRatio(
2424 childBox.horizontalBorderAndPaddingExtent(),
2425 childBox.verticalBorderAndPaddingExtent(),
2426 LayoutUnit { childBoxStyle.logicalAspectRatio() },
2427 childBoxStyle.boxSizingForAspectRatio(),
2428 LayoutUnit { fixedChildBoxStyleLogicalWidth->resolveZoom(childBoxStyle.usedZoomForLength()) },
2429 style().aspectRatio(),
2430 isRenderReplaced()
2431 );
2432 minPreferredLogicalWidth = aspectRatioSize;
2433 maxPreferredLogicalWidth = aspectRatioSize;
2434 return;
2435 }
2436 auto logicalHeightWithoutLayout = childBox.computeLogicalHeightWithoutLayout();
2437 minPreferredLogicalWidth = logicalHeightWithoutLayout;
2438 maxPreferredLogicalWidth = logicalHeightWithoutLayout;
2439 return;
2440 }
2441
2442 computeChildIntrinsicLogicalWidths(childBox, minPreferredLogicalWidth, maxPreferredLogicalWidth);
2443
2444 // For non-replaced blocks if the inline size is min|max-content or a definite
2445 // size the min|max-content contribution is that size plus border, padding and
2446 // margin https://drafts.csswg.org/css-sizing/#block-intrinsic
2447 if (!is<RenderBlock>(childBox))
2448 return;
2449 auto& computedInlineSize = childBox.style().logicalWidth();
2450 if (computedInlineSize.isMaxContent())
2451 minPreferredLogicalWidth = maxPreferredLogicalWidth;
2452 else if (computedInlineSize.isMinContent())
2453 maxPreferredLogicalWidth = minPreferredLogicalWidth;
2454}
2455
2456bool RenderBlock::hasLineIfEmpty() const
2457{
2458 RefPtr element = this->element();
2459 return element && element->isRootEditableElement();
2460}
2461
2462std::optional<LayoutUnit> RenderBlock::firstLineBaseline() const
2463{
2464 if (shouldApplyLayoutContainment())
2465 return { };
2466
2467 if (isWritingModeRoot() && !isFlexItem())
2468 return { };
2469
2470 auto firstInFlowBaseline = [&] -> std::optional<LayoutUnit> {
2471 for (CheckedPtr child = firstInFlowChildBox(); child; child = child->nextInFlowSiblingBox()) {
2472 if (child->isLegend() && child->isExcludedFromNormalLayout())
2473 continue;
2474 if (auto baseline = child->firstLineBaseline())
2475 return (settings().subpixelInlineLayoutEnabled() ? LayoutUnit(child->logicalTop()) : LayoutUnit(child->logicalTop().toInt())) + *baseline;
2476 }
2477 return { };
2478 };
2479 return firstInFlowBaseline();
2480}
2481
2482std::optional<LayoutUnit> RenderBlock::lastLineBaseline() const
2483{
2484 if (shouldApplyLayoutContainment())
2485 return { };
2486
2487 if (isWritingModeRoot() && !isFlexItem())
2488 return { };
2489
2490 auto lastInFlowBaseline = [&] -> std::optional<LayoutUnit> {
2491 for (CheckedPtr child = lastInFlowChildBox(); child; child = child->previousInFlowSiblingBox()) {
2492 if (child->isLegend() && child->isExcludedFromNormalLayout())
2493 continue;
2494 if (auto baseline = child->lastLineBaseline())
2495 return (settings().subpixelInlineLayoutEnabled() ? LayoutUnit(child->logicalTop()) : LayoutUnit(child->logicalTop().toInt())) + *baseline;
2496 }
2497 return { };
2498 };
2499 return lastInFlowBaseline();
2500}
2501
2502static inline bool NODELETE[[clang::annotate_type("webkit.nodelete")]] isRenderBlockFlowOrRenderButton(RenderElement& renderElement)
2503{
2504 // We include isRenderButton in this check because buttons are implemented
2505 // using flex box but should still support first-line|first-letter.
2506 // The flex box and specs require that flex box and grid do not support
2507 // first-line|first-letter, though.
2508 // FIXME: Remove when buttons are implemented with align-items instead of
2509 // flex box.
2510 return renderElement.isRenderBlockFlow() || renderElement.isRenderButton();
2511}
2512
2513static inline RenderBlock* findFirstLetterBlock(RenderBlock* start)
2514{
2515 RenderBlock* firstLetterBlock = start;
2516 while (true) {
2517 bool canHaveFirstLetterRenderer = firstLetterBlock->style().hasPseudoStyle(PseudoElementType::FirstLetter)
2518 && firstLetterBlock->canHaveGeneratedChildren()
2519 && isRenderBlockFlowOrRenderButton(*firstLetterBlock);
2520 if (canHaveFirstLetterRenderer)
2521 return firstLetterBlock;
2522
2523 RenderElement* parentBlock = firstLetterBlock->parent();
2524 if (firstLetterBlock->isBlockLevelReplacedOrAtomicInline() || !parentBlock || parentBlock->firstChild() != firstLetterBlock
2525 || !isRenderBlockFlowOrRenderButton(*parentBlock))
2526 return nullptr;
2527 firstLetterBlock = downcast<RenderBlock>(parentBlock);
2528 }
2529
2530 return nullptr;
2531}
2532
2533std::pair<RenderObject*, RenderElement*> RenderBlock::firstLetterAndContainer(RenderObject* skipThisAsFirstLetter)
2534{
2535 // Don't recur
2536 if (style().pseudoElementType() == PseudoElementType::FirstLetter)
2537 return { };
2538
2539 // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find
2540 // an efficient way to check for that situation though before implementing anything.
2541 RenderElement* firstLetterContainer = findFirstLetterBlock(this);
2542 if (!firstLetterContainer)
2543 return { };
2544
2545 // Drill into inlines looking for our first text descendant.
2546 auto* firstLetter = firstLetterContainer->firstChild();
2547 while (firstLetter) {
2548 if (is<RenderText>(*firstLetter)) {
2549 if (firstLetter == skipThisAsFirstLetter) {
2550 firstLetter = firstLetter->nextSibling();
2551 continue;
2552 }
2553 break;
2554 }
2555
2556 RenderElement& current = downcast<RenderElement>(*firstLetter);
2557 if (is<RenderListMarker>(current))
2558 firstLetter = current.nextSibling();
2559 else if (current.isFloatingOrOutOfFlowPositioned()) {
2560 if (current.style().pseudoElementType() == PseudoElementType::FirstLetter) {
2561 firstLetter = current.firstChild();
2562 break;
2563 }
2564 firstLetter = current.nextSibling();
2565 } else if (current.isBlockLevelReplacedOrAtomicInline() || isAnyOf<RenderButton, RenderMenuList>(current))
2566 break;
2567 else if (current.isFlexibleBoxIncludingDeprecated() || current.isRenderGrid())
2568 return { };
2569 else if (current.style().hasPseudoStyle(PseudoElementType::FirstLetter) && current.canHaveGeneratedChildren()) {
2570 // We found a lower-level node with first-letter, which supersedes the higher-level style
2571 firstLetterContainer = &current;
2572 firstLetter = current.firstChild();
2573 } else
2574 firstLetter = current.firstChild();
2575 }
2576
2577 if (!firstLetter)
2578 return { };
2579
2580 return { firstLetter, firstLetterContainer };
2581}
2582
2583RenderFragmentedFlow* RenderBlock::cachedEnclosingFragmentedFlow() const
2584{
2585 RenderBlockRareData* rareData = blockRareData();
2586
2587 if (!rareData || !rareData->m_enclosingFragmentedFlow)
2588 return nullptr;
2589
2590 return rareData->m_enclosingFragmentedFlow.value().get();
2591}
2592
2593bool RenderBlock::cachedEnclosingFragmentedFlowNeedsUpdate() const
2594{
2595 RenderBlockRareData* rareData = blockRareData();
2596
2597 if (!rareData || !rareData->m_enclosingFragmentedFlow)
2598 return true;
2599
2600 return false;
2601}
2602
2603void RenderBlock::setCachedEnclosingFragmentedFlowNeedsUpdate()
2604{
2605 RenderBlockRareData& rareData = ensureBlockRareData();
2606 rareData.m_enclosingFragmentedFlow = std::nullopt;
2607}
2608
2609RenderFragmentedFlow* RenderBlock::updateCachedEnclosingFragmentedFlow(RenderFragmentedFlow* fragmentedFlow) const
2610{
2611 if (!fragmentedFlow) {
2612 if (auto* rareData = const_cast<RenderBlock&>(*this).blockRareData())
2613 rareData->m_enclosingFragmentedFlow = { };
2614 return { };
2615 }
2616
2617 const_cast<RenderBlock&>(*this).ensureBlockRareData().m_enclosingFragmentedFlow = fragmentedFlow;
2618 return fragmentedFlow;
2619}
2620
2621RenderFragmentedFlow* RenderBlock::locateEnclosingFragmentedFlow() const
2622{
2623 RenderBlockRareData* rareData = blockRareData();
2624 if (!rareData || !rareData->m_enclosingFragmentedFlow)
2625 return updateCachedEnclosingFragmentedFlow(RenderBox::locateEnclosingFragmentedFlow());
2626
2627 ASSERT(rareData->m_enclosingFragmentedFlow.value() == RenderBox::locateEnclosingFragmentedFlow())((void)0);
2628 return rareData->m_enclosingFragmentedFlow.value().get();
2629}
2630
2631void RenderBlock::resetEnclosingFragmentedFlowAndChildInfoIncludingDescendants(RenderFragmentedFlow* fragmentedFlow)
2632{
2633 if (fragmentedFlowState() == FragmentedFlowState::NotInsideFlow)
2634 return;
2635
2636 if (auto* cachedFragmentedFlow = cachedEnclosingFragmentedFlow())
2637 fragmentedFlow = cachedFragmentedFlow;
2638 setCachedEnclosingFragmentedFlowNeedsUpdate();
2639 RenderElement::resetEnclosingFragmentedFlowAndChildInfoIncludingDescendants(fragmentedFlow);
2640}
2641
2642LayoutUnit RenderBlock::paginationStrut() const
2643{
2644 RenderBlockRareData* rareData = blockRareData();
2645 return rareData ? rareData->m_paginationStrut : 0_lu;
2646}
2647
2648LayoutUnit RenderBlock::pageLogicalOffset() const
2649{
2650 RenderBlockRareData* rareData = blockRareData();
2651 return rareData ? rareData->m_pageLogicalOffset : 0_lu;
2652}
2653
2654void RenderBlock::setPaginationStrut(LayoutUnit strut)
2655{
2656 RenderBlockRareData* rareData = blockRareData();
2657 if (!rareData) {
2658 if (!strut)
2659 return;
2660 rareData = &ensureBlockRareData();
2661 }
2662 rareData->m_paginationStrut = strut;
2663}
2664
2665void RenderBlock::setPageLogicalOffset(LayoutUnit logicalOffset)
2666{
2667 RenderBlockRareData* rareData = blockRareData();
2668 if (!rareData) {
2669 if (!logicalOffset)
2670 return;
2671 rareData = &ensureBlockRareData();
2672 }
2673 rareData->m_pageLogicalOffset = logicalOffset;
2674}
2675
2676void RenderBlock::boundingRects(Vector<LayoutRect>& rects, const LayoutPoint& accumulatedOffset) const
2677{
2678 // For blocks inside inlines, we include margins so that we run right up to the inline boxes
2679 // above and below us (thus getting merged with them to form a single irregular shape).
2680 if (auto* continuation = this->continuation()) {
2681 // FIXME: This is wrong for block-flows that are horizontal.
2682 // https://bugs.webkit.org/show_bug.cgi?id=46781
2683 rects.append(LayoutRect(accumulatedOffset.x(), accumulatedOffset.y() - collapsedMarginBefore(), width(), height() + collapsedMarginBefore() + collapsedMarginAfter()));
2684 auto* containingBlock = inlineContinuation()->containingBlock();
2685 continuation->boundingRects(rects, accumulatedOffset - locationOffset() + containingBlock->locationOffset());
2686 } else
2687 rects.append({ accumulatedOffset, size() });
2688}
2689
2690void RenderBlock::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
2691{
2692 if (!continuation()) {
2693 absoluteQuadsIgnoringContinuation({ { }, size() }, quads, wasFixed);
2694 return;
2695 }
2696 // For blocks inside inlines, we include margins so that we run right up to the inline boxes
2697 // above and below us (thus getting merged with them to form a single irregular shape).
2698 auto logicalRect = FloatRect { 0, -collapsedMarginBefore(), width(), height() + collapsedMarginBefore() + collapsedMarginAfter() };
2699 absoluteQuadsIgnoringContinuation(logicalRect, quads, wasFixed);
2700 collectAbsoluteQuadsForContinuation(quads, wasFixed);
2701}
2702
2703void RenderBlock::absoluteQuadsIgnoringContinuation(const FloatRect& logicalRect, Vector<FloatQuad>& quads, bool* wasFixed) const
2704{
2705 // FIXME: This is wrong for block-flows that are horizontal.
2706 // https://bugs.webkit.org/show_bug.cgi?id=46781
2707 CheckedPtr fragmentedFlow = enclosingFragmentedFlow();
2708 if (!fragmentedFlow || !fragmentedFlow->absoluteQuadsForBox(quads, wasFixed, *this))
2709 quads.append(localToAbsoluteQuad(logicalRect, UseTransforms, wasFixed));
2710}
2711
2712LayoutRect RenderBlock::rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const
2713{
2714 LayoutRect r(RenderBox::rectWithOutlineForRepaint(repaintContainer, outlineWidth));
2715 if (isContinuation())
2716 r.inflateY(collapsedMarginBefore()); // FIXME: This is wrong for block-flows that are horizontal.
2717 return r;
2718}
2719
2720const RenderStyle& RenderBlock::outlineStyleForRepaint() const
2721{
2722 if (auto* continuation = this->continuation())
2723 return continuation->style();
2724 return RenderElement::outlineStyleForRepaint();
2725}
2726
2727LayoutUnit RenderBlock::offsetFromLogicalTopOfFirstPage() const
2728{
2729 auto* layoutState = view().frameView().layoutContext().layoutState();
2730 if (layoutState && !layoutState->isPaginated())
2731 return 0;
2732
2733 if (CheckedPtr fragmentedFlow = enclosingFragmentedFlow())
2734 return fragmentedFlow->offsetFromLogicalTopOfFirstFragment(this);
2735
2736 if (layoutState) {
2737 ASSERT(layoutState->renderer() == this)((void)0);
2738
2739 LayoutSize offsetDelta = layoutState->layoutOffset() - layoutState->pageOffset();
2740 return isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width();
2741 }
2742
2743 ASSERT_NOT_REACHED()((void)0);
2744 return 0;
2745}
2746
2747RenderFragmentContainer* RenderBlock::fragmentAtBlockOffset(LayoutUnit blockOffset) const
2748{
2749 if (CheckedPtr fragmentedFlow = enclosingFragmentedFlow(); fragmentedFlow && fragmentedFlow->hasValidFragmentInfo())
2750 return fragmentedFlow->fragmentAtBlockOffset(this, offsetFromLogicalTopOfFirstPage() + blockOffset, true);
2751
2752 return nullptr;
2753}
2754
2755static bool canComputeFragmentRangeForBox(const RenderBlock& parentBlock, const RenderBox& childBox, const RenderFragmentedFlow& enclosingFragmentedFlow)
2756{
2757 if (!enclosingFragmentedFlow.hasFragments())
2758 return false;
2759
2760 if (!childBox.canHaveOutsideFragmentRange())
2761 return false;
2762
2763 return enclosingFragmentedFlow.hasCachedFragmentRangeForBox(parentBlock);
2764}
2765
2766bool RenderBlock::childBoxIsUnsplittableForFragmentation(const RenderBox& child) const
2767{
2768 CheckedPtr fragmentedFlow = enclosingFragmentedFlow();
2769 bool checkColumnBreaks = fragmentedFlow && fragmentedFlow->shouldCheckColumnBreaks();
2770 bool checkPageBreaks = !checkColumnBreaks && view().frameView().layoutContext().layoutState()->pageLogicalHeight();
2771 return child.isUnsplittableForPagination() || child.style().breakInside() == BreakInside::Avoid
2772 || (checkColumnBreaks && child.style().breakInside() == BreakInside::AvoidColumn)
2773 || (checkPageBreaks && child.style().breakInside() == BreakInside::AvoidPage);
2774}
2775
2776void RenderBlock::computeFragmentRangeForBoxChild(const RenderBox& box) const
2777{
2778 CheckedPtr fragmentedFlow = enclosingFragmentedFlow();
2779 ASSERT(fragmentedFlow && canComputeFragmentRangeForBox(*this, box, *fragmentedFlow))((void)0);
2780 ASSERT(box.fragmentedFlowState() == FragmentedFlowState::InsideFlow)((void)0);
2781
2782 RenderFragmentContainer* startFragment;
2783 RenderFragmentContainer* endFragment;
2784 LayoutUnit offsetFromLogicalTopOfFirstFragment = box.offsetFromLogicalTopOfFirstPage();
2785 if (childBoxIsUnsplittableForFragmentation(box))
2786 startFragment = endFragment = fragmentedFlow->fragmentAtBlockOffset(this, offsetFromLogicalTopOfFirstFragment, true);
2787 else {
2788 startFragment = fragmentedFlow->fragmentAtBlockOffset(this, offsetFromLogicalTopOfFirstFragment, true);
2789 endFragment = fragmentedFlow->fragmentAtBlockOffset(this, offsetFromLogicalTopOfFirstFragment + logicalHeightForChild(box), true);
2790 }
2791
2792 fragmentedFlow->setFragmentRangeForBox(box, startFragment, endFragment);
2793}
2794
2795void RenderBlock::estimateFragmentRangeForBoxChild(const RenderBox& box) const
2796{
2797 CheckedPtr fragmentedFlow = enclosingFragmentedFlow();
2798 if (!fragmentedFlow || box.fragmentedFlowState() == FragmentedFlowState::NotInsideFlow || !canComputeFragmentRangeForBox(*this, box, *fragmentedFlow))
2799 return;
2800
2801 if (childBoxIsUnsplittableForFragmentation(box)) {
2802 computeFragmentRangeForBoxChild(box);
2803 return;
2804 }
2805
2806 auto estimatedValues = box.computeLogicalHeight(RenderFragmentedFlow::maxLogicalHeight(), logicalTopForChild(box));
2807 LayoutUnit offsetFromLogicalTopOfFirstFragment = box.offsetFromLogicalTopOfFirstPage();
2808 RenderFragmentContainer* startFragment = fragmentedFlow->fragmentAtBlockOffset(this, offsetFromLogicalTopOfFirstFragment, true);
2809 RenderFragmentContainer* endFragment = fragmentedFlow->fragmentAtBlockOffset(this, offsetFromLogicalTopOfFirstFragment + estimatedValues.extent, true);
2810
2811 fragmentedFlow->setFragmentRangeForBox(box, startFragment, endFragment);
2812}
2813
2814bool RenderBlock::updateFragmentRangeForBoxChild(const RenderBox& box) const
2815{
2816 CheckedPtr fragmentedFlow = enclosingFragmentedFlow();
2817 if (!fragmentedFlow || box.fragmentedFlowState() == FragmentedFlowState::NotInsideFlow || !canComputeFragmentRangeForBox(*this, box, *fragmentedFlow))
2818 return false;
2819
2820 RenderFragmentContainer* startFragment = nullptr;
2821 RenderFragmentContainer* endFragment = nullptr;
2822 fragmentedFlow->getFragmentRangeForBox(box, startFragment, endFragment);
2823
2824 computeFragmentRangeForBoxChild(box);
2825
2826 RenderFragmentContainer* newStartFragment = nullptr;
2827 RenderFragmentContainer* newEndFragment = nullptr;
2828 fragmentedFlow->getFragmentRangeForBox(box, newStartFragment, newEndFragment);
2829
2830
2831 // Changing the start fragment means we shift everything and a relayout is needed.
2832 if (newStartFragment != startFragment)
2833 return true;
2834
2835 // The fragment range of the box has changed. Some boxes (e.g floats) may have been positioned assuming
2836 // a different range.
2837 if (box.needsLayoutAfterFragmentRangeChange() && newEndFragment != endFragment)
2838 return true;
2839
2840 return false;
2841}
2842
2843void RenderBlock::setTrimmedMarginForChild(RenderBox& child, Style::MarginTrimSide side)
2844{
2845 switch (side) {
2846 case Style::MarginTrimSide::BlockStart:
2847 setMarginBeforeForChild(child, 0_lu);
2848 break;
2849 case Style::MarginTrimSide::BlockEnd:
2850 setMarginAfterForChild(child, 0_lu);
2851 break;
2852 case Style::MarginTrimSide::InlineStart:
2853 setMarginStartForChild(child, 0_lu);
2854 break;
2855 case Style::MarginTrimSide::InlineEnd:
2856 setMarginEndForChild(child, 0_lu);
2857 break;
2858 default:
2859 ASSERT_NOT_IMPLEMENTED_YET()((void)0);
2860 }
2861
2862 child.markMarginAsTrimmed(side);
2863}
2864
2865LayoutUnit RenderBlock::collapsedMarginBeforeForChild(const RenderBox& child) const
2866{
2867 // If the child has the same directionality as we do, then we can just return its
2868 // collapsed margin.
2869 if (!child.isWritingModeRoot())
2870 return child.collapsedMarginBefore();
2871
2872 // The child has a different directionality. If the child is parallel, then it's just
2873 // flipped relative to us. We can use the collapsed margin for the opposite edge.
2874 if (child.isHorizontalWritingMode() == isHorizontalWritingMode())
2875 return child.collapsedMarginAfter();
2876
2877 // The child is perpendicular to us, which means its margins don't collapse but are on the
2878 // "logical left/right" sides of the child box. We can just return the raw margin in this case.
2879 return marginBeforeForChild(child);
2880}
2881
2882LayoutUnit RenderBlock::collapsedMarginAfterForChild(const RenderBox& child) const
2883{
2884 // If the child has the same directionality as we do, then we can just return its
2885 // collapsed margin.
2886 if (!child.isWritingModeRoot())
2887 return child.collapsedMarginAfter();
2888
2889 // The child has a different directionality. If the child is parallel, then it's just
2890 // flipped relative to us. We can use the collapsed margin for the opposite edge.
2891 if (child.isHorizontalWritingMode() == isHorizontalWritingMode())
2892 return child.collapsedMarginBefore();
2893
2894 // The child is perpendicular to us, which means its margins don't collapse but are on the
2895 // "logical left/right" side of the child box. We can just return the raw margin in this case.
2896 return marginAfterForChild(child);
2897}
2898
2899bool RenderBlock::hasMarginBeforeQuirk(const RenderBox& child) const
2900{
2901 // If the child has the same directionality as we do, then we can just return its
2902 // margin quirk.
2903 if (!child.isWritingModeRoot()) {
2904 auto* childBlock = dynamicDowncast<RenderBlock>(child);
2905 return childBlock ? childBlock->hasMarginBeforeQuirk() : child.style().marginBefore().hasQuirk();
2906 }
2907
2908 // The child has a different directionality. If the child is parallel, then it's just
2909 // flipped relative to us. We can use the opposite edge.
2910 if (child.isHorizontalWritingMode() == isHorizontalWritingMode()) {
2911 auto* childBlock = dynamicDowncast<RenderBlock>(child);
2912 return childBlock ? childBlock->hasMarginAfterQuirk() : child.style().marginAfter().hasQuirk();
2913 }
2914
2915 // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about
2916 // whether or not authors specified quirky ems, since they're an implementation detail.
2917 return false;
2918}
2919
2920bool RenderBlock::hasMarginAfterQuirk(const RenderBox& child) const
2921{
2922 // If the child has the same directionality as we do, then we can just return its
2923 // margin quirk.
2924 if (!child.isWritingModeRoot()) {
2925 auto* childBlock = dynamicDowncast<RenderBlock>(child);
2926 return childBlock ? childBlock->hasMarginAfterQuirk() : child.style().marginAfter().hasQuirk();
2927 }
2928
2929 // The child has a different directionality. If the child is parallel, then it's just
2930 // flipped relative to us. We can use the opposite edge.
2931 if (child.isHorizontalWritingMode() == isHorizontalWritingMode()) {
2932 auto* childBlock = dynamicDowncast<RenderBlock>(child);
2933 return childBlock ? childBlock->hasMarginBeforeQuirk() : child.style().marginBefore().hasQuirk();
2934 }
2935
2936 // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about
2937 // whether or not authors specified quirky ems, since they're an implementation detail.
2938 return false;
2939}
2940
2941ASCIILiteral RenderBlock::renderName() const
2942{
2943 if (isBody())
2944 return "RenderBody"_s; // FIXME: Temporary hack until we know that the regression tests pass.
2945 if (isFieldset())
2946 return "RenderFieldSet"_s; // FIXME: Remove eventually, but done to keep tests from breaking.
2947 if (isFloating())
2948 return "RenderBlock (floating)"_s;
2949 if (isOutOfFlowPositioned())
2950 return "RenderBlock (positioned)"_s;
2951 if (isAnonymousBlock())
2952 return "RenderBlock (anonymous)"_s;
2953 // FIXME: Temporary hack while the new generated content system is being implemented.
2954 if (isPseudoElement())
2955 return "RenderBlock (generated)"_s;
2956 if (isAnonymous())
2957 return "RenderBlock (generated)"_s;
2958 if (isRelativelyPositioned())
2959 return "RenderBlock (relative positioned)"_s;
2960 if (isStickilyPositioned())
2961 return "RenderBlock (sticky positioned)"_s;
2962 return "RenderBlock"_s;
2963}
2964
2965String RenderBlock::debugDescription() const
2966{
2967 if (isViewTransitionPseudo()) {
2968 StringBuilder builder;
2969
2970 builder.append(renderName(), " 0x"_s, hex(reinterpret_cast<uintptr_t>(this), Lowercase));
2971
2972 builder.append(" ::view-transition"_s);
2973 if (style().pseudoElementType() != PseudoElementType::ViewTransition) {
2974 builder.append("-"_s, style().pseudoElementType() == PseudoElementType::ViewTransitionGroup ? "group("_s : "image-pair("_s);
2975 builder.append(style().pseudoElementNameArgument(), ')');
2976 }
2977 return builder.toString();
2978 }
2979
2980 return RenderObject::debugDescription();
2981}
2982
2983TextRun RenderBlock::constructTextRun(StringView stringView, const RenderStyle& style, ExpansionBehavior expansion, TextRunFlags flags)
2984{
2985 auto textDirection = TextDirection::LTR;
2986 bool directionalOverride = style.rtlOrdering() == Order::Visual;
2987 if (flags != DefaultTextRunFlags) {
2988 if (flags & RespectDirection)
2989 textDirection = style.writingMode().bidiDirection();
2990 if (flags & RespectDirectionOverride)
2991 directionalOverride |= isOverride(style.unicodeBidi());
2992 }
2993
2994 // This works because:
2995 // 1. TextRun owns its text string. Its member is a String, not a StringView
2996 // 2. This replacement doesn't affect string indices. We're replacing a single Unicode code unit with another Unicode code unit.
2997 // How convenient.
2998 auto updatedString = RenderBlock::updateSecurityDiscCharacters(style, stringView.toStringWithoutCopying());
2999 return TextRun(WTF::move(updatedString), 0, 0, expansion, textDirection, directionalOverride);
3000}
3001
3002TextRun RenderBlock::constructTextRun(const String& string, const RenderStyle& style, ExpansionBehavior expansion, TextRunFlags flags)
3003{
3004 return constructTextRun(StringView(string), style, expansion, flags);
3005}
3006
3007TextRun RenderBlock::constructTextRun(const AtomString& atomString, const RenderStyle& style, ExpansionBehavior expansion, TextRunFlags flags)
3008{
3009 return constructTextRun(StringView(atomString), style, expansion, flags);
3010}
3011
3012TextRun RenderBlock::constructTextRun(const RenderText& text, const RenderStyle& style, ExpansionBehavior expansion)
3013{
3014 return constructTextRun(text.stringView(), style, expansion);
3015}
3016
3017TextRun RenderBlock::constructTextRun(const RenderText& text, unsigned offset, unsigned length, const RenderStyle& style, ExpansionBehavior expansion)
3018{
3019 unsigned stop = offset + length;
3020 ASSERT(stop <= text.text().length())((void)0);
3021 return constructTextRun(text.stringView(offset, stop), style, expansion);
3022}
3023
3024TextRun RenderBlock::constructTextRun(std::span<const Latin1Character> characters, const RenderStyle& style, ExpansionBehavior expansion)
3025{
3026 return constructTextRun(StringView { characters }, style, expansion);
3027}
3028
3029TextRun RenderBlock::constructTextRun(std::span<const char16_t> characters, const RenderStyle& style, ExpansionBehavior expansion)
3030{
3031 return constructTextRun(StringView { characters }, style, expansion);
3032}
3033
3034bool RenderBlock::hasDefiniteLogicalHeight() const
3035{
3036 return (bool)availableLogicalHeightForPercentageComputation();
3037}
3038
3039std::optional<LayoutUnit> RenderBlock::availableLogicalHeightForPercentageComputation() const
3040{
3041 // For anonymous blocks that are skipped during percentage height calculation,
3042 // we consider them to have an indefinite height.
3043 if (skipContainingBlockForPercentHeightCalculation(*this, false))
3044 return { };
3045
3046 auto availableHeight = [&]() -> std::optional<LayoutUnit> {
3047 if (auto overridingLogicalHeightForFlex = (isFlexItem() ? downcast<RenderFlexibleBox>(parent())->usedFlexItemOverridingLogicalHeightForPercentageResolution(*this) : std::nullopt))
3048 return contentBoxLogicalHeight(*overridingLogicalHeightForFlex);
3049
3050 if (auto overridingLogicalHeightForGrid = (isGridItem() ? overridingBorderBoxLogicalHeight() : std::nullopt))
3051 return contentBoxLogicalHeight(*overridingLogicalHeightForGrid);
3052
3053 auto& style = this->style();
3054 if (auto fixedLogicalHeight = style.logicalHeight().tryFixed()) {
3055 auto contentBoxHeight = adjustContentBoxLogicalHeightForBoxSizing(LayoutUnit { fixedLogicalHeight->resolveZoom(style.usedZoomForLength()) });
3056 return std::max(0_lu, constrainContentBoxLogicalHeightByMinMax(contentBoxHeight - scrollbarLogicalHeight(), { }));
3057 }
3058
3059 if (shouldComputeLogicalHeightFromAspectRatio()) {
3060 // Only grid is expected to be in a state where it is calculating pref width and having unknown logical width.
3061 if (isRenderGrid() && needsPreferredLogicalWidthsUpdate() && !style.logicalWidth().isSpecified())
3062 return { };
3063 return blockSizeFromAspectRatio(
3064 horizontalBorderAndPaddingExtent(),
3065 verticalBorderAndPaddingExtent(),
3066 LayoutUnit { style.logicalAspectRatio() },
3067 style.boxSizingForAspectRatio(),
3068 logicalWidth(),
3069 style.aspectRatio(),
3070 isRenderReplaced()
3071 );
3072 }
3073
3074 // A positioned element that specified both top/bottom or that specifies
3075 // height should be treated as though it has a height explicitly specified
3076 // that can be used for any percentage computations.
3077 // However, intrinsic heights (fit-content, min-content, max-content) are
3078 // content-dependent and should be treated as indefinite for percentage
3079 // resolution of children, since the actual height is not yet determined.
3080 auto heightIsIntrinsic = style.logicalHeight().isIntrinsic() || style.logicalHeight().isLegacyIntrinsic();
3081 auto hasNonIntrinsicSpecifiedHeight = !style.logicalHeight().isAuto() && !heightIsIntrinsic;
3082 auto hasDefiniteHeightFromInsets = !style.logicalTop().isAuto() && !style.logicalBottom().isAuto() && style.logicalHeight().isAuto();
3083 auto isOutOfFlowPositionedWithSpecifiedHeight = isOutOfFlowPositioned() && (hasNonIntrinsicSpecifiedHeight || hasDefiniteHeightFromInsets);
3084 if (isOutOfFlowPositionedWithSpecifiedHeight) {
3085 // Don't allow this to affect the block' size() member variable, since this
3086 // can get called while the block is still laying out its kids.
3087 return std::max(0_lu, computeLogicalHeight(logicalHeight(), 0_lu).extent - borderAndPaddingLogicalHeight() - scrollbarLogicalHeight());
3088 }
3089
3090 if (style.logicalHeight().isPercentOrCalculated()) {
3091 if (auto heightWithScrollbar = computePercentageLogicalHeight(style.logicalHeight())) {
3092 auto contentBoxHeightWithScrollbar = adjustContentBoxLogicalHeightForBoxSizing(*heightWithScrollbar);
3093 // We need to adjust for min/max height because this method does not handle the min/max of the current block, its caller does.
3094 // So the return value from the recursive call will not have been adjusted yet.
3095 return std::max(0_lu, constrainContentBoxLogicalHeightByMinMax(contentBoxHeightWithScrollbar - scrollbarLogicalHeight(), { }));
3096 }
3097 return { };
3098 }
3099
3100 if (isRenderView())
3101 return view().pageOrViewLogicalHeight();
3102
3103 return { };
3104 };
3105 return availableHeight();
3106}
3107
3108void RenderBlock::layoutExcludedChildren(RelayoutChildren relayoutChildren)
3109{
3110 if (!isFieldset())
3111 return;
3112
3113 setIntrinsicBorderForFieldset(0);
3114
3115 RenderBox* box = findFieldsetLegend();
3116 if (!box)
3117 return;
3118
3119 box->setIsExcludedFromNormalLayout(true);
3120 for (auto& child : childrenOfType<RenderBox>(*this)) {
3121 if (&child == box || !child.isLegend())
3122 continue;
3123 child.setIsExcludedFromNormalLayout(false);
3124 }
3125
3126 RenderBox& legend = *box;
3127 if (relayoutChildren == RelayoutChildren::Yes)
3128 legend.setChildNeedsLayout(MarkOnlyThis);
3129 legend.layoutIfNeeded();
3130
3131 LayoutUnit logicalLeft;
3132 if (writingMode().isBidiLTR()) {
3133 switch (legend.style().textAlign()) {
3134 case Style::TextAlign::Center:
3135 logicalLeft = (logicalWidth() - logicalWidthForChild(legend)) / 2;
3136 break;
3137 case Style::TextAlign::Right:
3138 logicalLeft = logicalWidth() - borderAndPaddingEnd() - logicalWidthForChild(legend);
3139 break;
3140 default:
3141 logicalLeft = borderAndPaddingStart() + marginStartForChild(legend);
3142 break;
3143 }
3144 } else {
3145 switch (legend.style().textAlign()) {
3146 case Style::TextAlign::Left:
3147 logicalLeft = borderAndPaddingStart();
3148 break;
3149 case Style::TextAlign::Center: {
3150 // Make sure that the extra pixel goes to the end side in RTL (since it went to the end side
3151 // in LTR).
3152 LayoutUnit centeredWidth = logicalWidth() - logicalWidthForChild(legend);
3153 logicalLeft = centeredWidth - centeredWidth / 2;
3154 break;
3155 }
3156 default:
3157 logicalLeft = logicalWidth() - borderAndPaddingStart() - marginStartForChild(legend) - logicalWidthForChild(legend);
3158 break;
3159 }
3160 }
3161
3162 setLogicalLeftForChild(legend, logicalLeft);
3163
3164 LayoutUnit fieldsetBorderBefore = borderBefore();
3165 LayoutUnit legendLogicalHeight = logicalHeightForChild(legend);
3166 LayoutUnit legendAfterMargin = marginAfterForChild(legend);
3167 LayoutUnit topPositionForLegend = std::max(0_lu, (fieldsetBorderBefore - legendLogicalHeight) / 2);
3168 LayoutUnit bottomPositionForLegend = topPositionForLegend + legendLogicalHeight + legendAfterMargin;
3169
3170 // Place the legend now.
3171 setLogicalTopForChild(legend, topPositionForLegend);
3172
3173 // If the bottom of the legend (including its after margin) is below the fieldset border,
3174 // then we need to add in sufficient intrinsic border to account for this gap.
3175 // FIXME: Should we support the before margin of the legend? Not entirely clear.
3176 // FIXME: Consider dropping support for the after margin of the legend. Not sure other
3177 // browsers support that anyway.
3178 if (bottomPositionForLegend > fieldsetBorderBefore)
3179 setIntrinsicBorderForFieldset(bottomPositionForLegend - fieldsetBorderBefore);
3180
3181 // Now that the legend is included in the border extent, we can set our logical height
3182 // to the borderBefore (which includes the legend and its after margin if they were bigger
3183 // than the actual fieldset border) and then add in our padding before.
3184 setLogicalHeight(borderAndPaddingBefore());
3185}
3186
3187RenderBox* RenderBlock::findFieldsetLegend(FieldsetFindLegendOption option) const
3188{
3189 if (isSkippedContentRoot(*this))
3190 return { };
3191
3192 for (auto& legend : childrenOfType<RenderBox>(*this)) {
3193 if (option == FieldsetIgnoreFloatingOrOutOfFlow && legend.isFloatingOrOutOfFlowPositioned())
3194 continue;
3195 if (legend.isLegend())
3196 return const_cast<RenderBox*>(&legend);
3197 }
3198 return { };
3199}
3200
3201void RenderBlock::adjustBorderBoxRectForPainting(LayoutRect& paintRect)
3202{
3203 if (!isFieldset() || !intrinsicBorderForFieldset())
3204 return;
3205
3206 auto* legend = findFieldsetLegend();
3207 if (!legend)
3208 return;
3209
3210 if (writingMode().isHorizontal()) {
3211 LayoutUnit yOff = std::max(0_lu, (legend->height() - RenderBox::borderBefore()) / 2);
3212 paintRect.setHeight(paintRect.height() - yOff);
3213 if (writingMode().isBlockTopToBottom())
3214 paintRect.setY(paintRect.y() + yOff);
3215 } else {
3216 LayoutUnit xOff = std::max(0_lu, (legend->width() - RenderBox::borderBefore()) / 2);
3217 paintRect.setWidth(paintRect.width() - xOff);
3218 if (writingMode().isBlockLeftToRight())
3219 paintRect.setX(paintRect.x() + xOff);
3220 }
3221}
3222
3223LayoutRect RenderBlock::paintRectToClipOutFromBorder(const LayoutRect& paintRect)
3224{
3225 LayoutRect clipRect;
3226 if (!isFieldset())
3227 return clipRect;
3228 auto* legend = findFieldsetLegend();
3229 if (!legend)
3230 return clipRect;
3231
3232 LayoutUnit borderExtent = RenderBox::borderBefore();
3233 if (writingMode().isHorizontal()) {
3234 clipRect.setX(paintRect.x() + legend->x());
3235 clipRect.setY(writingMode().isBlockTopToBottom() ? paintRect.y() : paintRect.y() + paintRect.height() - borderExtent);
3236 clipRect.setWidth(legend->width());
3237 clipRect.setHeight(borderExtent);
3238 } else {
3239 clipRect.setX(writingMode().isBlockLeftToRight() ? paintRect.x() : paintRect.x() + paintRect.width() - borderExtent);
3240 clipRect.setY(paintRect.y() + legend->y());
3241 clipRect.setWidth(borderExtent);
3242 clipRect.setHeight(legend->height());
3243 }
3244 return clipRect;
3245}
3246
3247LayoutUnit RenderBlock::intrinsicBorderForFieldset() const
3248{
3249 auto* rareData = blockRareData();
3250 return rareData ? rareData->m_intrinsicBorderForFieldset : 0_lu;
3251}
3252
3253void RenderBlock::setIntrinsicBorderForFieldset(LayoutUnit padding)
3254{
3255 auto* rareData = blockRareData();
3256 if (!rareData) {
3257 if (!padding)
3258 return;
3259 rareData = &ensureBlockRareData();
3260 }
3261 rareData->m_intrinsicBorderForFieldset = padding;
3262}
3263
3264RectEdges<LayoutUnit> RenderBlock::borderWidths() const
3265{
3266 if (!intrinsicBorderForFieldset())
3267 return RenderBox::borderWidths();
3268
3269 return {
3270 borderTop(),
3271 borderRight(),
3272 borderBottom(),
3273 borderLeft()
3274 };
3275}
3276
3277LayoutUnit RenderBlock::borderTop() const
3278{
3279 if (!writingMode().isBlockTopToBottom() || !intrinsicBorderForFieldset())
3280 return RenderBox::borderTop();
3281 return RenderBox::borderTop() + intrinsicBorderForFieldset();
3282}
3283
3284LayoutUnit RenderBlock::borderLeft() const
3285{
3286 if (!writingMode().isBlockLeftToRight() || !intrinsicBorderForFieldset())
3287 return RenderBox::borderLeft();
3288 return RenderBox::borderLeft() + intrinsicBorderForFieldset();
3289}
3290
3291LayoutUnit RenderBlock::borderBottom() const
3292{
3293 if (writingMode().blockDirection() != FlowDirection::BottomToTop || !intrinsicBorderForFieldset())
3294 return RenderBox::borderBottom();
3295 return RenderBox::borderBottom() + intrinsicBorderForFieldset();
3296}
3297
3298LayoutUnit RenderBlock::borderRight() const
3299{
3300 if (writingMode().blockDirection() != FlowDirection::RightToLeft || !intrinsicBorderForFieldset())
3301 return RenderBox::borderRight();
3302 return RenderBox::borderRight() + intrinsicBorderForFieldset();
3303}
3304
3305LayoutUnit RenderBlock::borderBefore() const
3306{
3307 return RenderBox::borderBefore() + intrinsicBorderForFieldset();
3308}
3309
3310bool RenderBlock::computePreferredWidthsForExcludedChildren(LayoutUnit& minWidth, LayoutUnit& maxWidth) const
3311{
3312 if (!isFieldset())
3313 return false;
3314
3315 auto* legend = findFieldsetLegend();
3316 if (!legend)
3317 return false;
3318
3319 legend->setIsExcludedFromNormalLayout(true);
3320
3321 computeChildPreferredLogicalWidths(*legend, minWidth, maxWidth);
3322
3323 // These are going to be added in later, so we subtract them out to reflect the
3324 // fact that the legend is outside the scrollable area.
3325 auto scrollbarWidth = intrinsicScrollbarLogicalWidthIncludingGutter();
3326 minWidth -= scrollbarWidth;
3327 maxWidth -= scrollbarWidth;
3328
3329 const auto& childStyle = legend->style();
3330 const auto& childZoomFactor = childStyle.usedZoomForLength();
3331
3332 LayoutUnit marginStart;
3333 LayoutUnit marginEnd;
3334 if (auto fixedMarginStart = childStyle.marginStart(writingMode()).tryFixed())
3335 marginStart += fixedMarginStart->resolveZoom(childZoomFactor);
3336 if (auto fixedMarginEnd = childStyle.marginEnd(writingMode()).tryFixed())
3337 marginEnd += fixedMarginEnd->resolveZoom(childZoomFactor);
3338
3339 auto margin = marginStart + marginEnd;
3340
3341 minWidth += margin;
3342 maxWidth += margin;
3343
3344 return true;
3345}
3346
3347LayoutUnit RenderBlock::adjustBorderBoxLogicalHeightForBoxSizing(LayoutUnit height) const
3348{
3349 // FIXME: We're doing this to match other browsers even though it's questionable.
3350 // Shouldn't height:100px mean the fieldset content gets 100px of height even if the
3351 // resulting fieldset becomes much taller because of the legend?
3352 LayoutUnit bordersPlusPadding = borderAndPaddingLogicalHeight();
3353 if (style().boxSizing() == BoxSizing::ContentBox)
3354 return height + bordersPlusPadding - intrinsicBorderForFieldset();
3355 return std::max(height, bordersPlusPadding);
3356}
3357
3358LayoutUnit RenderBlock::adjustContentBoxLogicalHeightForBoxSizing(std::optional<LayoutUnit> height) const
3359{
3360 // FIXME: We're doing this to match other browsers even though it's questionable.
3361 // Shouldn't height:100px mean the fieldset content gets 100px of height even if the
3362 // resulting fieldset becomes much taller because of the legend?
3363 if (!height)
3364 return 0;
3365 LayoutUnit result = height.value();
3366 if (style().boxSizing() == BoxSizing::BorderBox)
3367 result -= borderAndPaddingLogicalHeight();
3368 else
3369 result -= intrinsicBorderForFieldset();
3370 return std::max(0_lu, result);
3371}
3372
3373LayoutUnit RenderBlock::adjustIntrinsicLogicalHeightForBoxSizing(LayoutUnit height) const
3374{
3375 if (style().boxSizing() == BoxSizing::BorderBox)
3376 return height + borderAndPaddingLogicalHeight();
3377 return height + intrinsicBorderForFieldset();
3378}
3379
3380void RenderBlock::paintExcludedChildrenInBorder(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
3381{
3382 if (!isFieldset())
3383 return;
3384
3385 RenderBox* box = findFieldsetLegend();
3386 if (!box || !box->isExcludedFromNormalLayout() || box->hasSelfPaintingLayer())
3387 return;
3388
3389 LayoutPoint childPoint = flipForWritingModeForChild(*box, paintOffset);
3390 box->paintAsInlineBlock(paintInfo, childPoint);
3391}
3392
3393bool RenderBlock::hitTestExcludedChildrenInBorder(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
3394{
3395 if (!isFieldset())
3396 return false;
3397
3398 auto* legend = findFieldsetLegend();
3399 if (!legend || !legend->isExcludedFromNormalLayout() || legend->hasSelfPaintingLayer())
3400 return false;
3401
3402 HitTestAction childHitTest = hitTestAction;
3403 if (hitTestAction == HitTestChildBlockBackgrounds)
3404 childHitTest = HitTestChildBlockBackground;
3405 LayoutPoint childPoint = flipForWritingModeForChild(*legend, accumulatedOffset);
3406 return legend->nodeAtPoint(request, result, locationInContainer, childPoint, childHitTest);
3407}
3408
3409String RenderBlock::updateSecurityDiscCharacters(const RenderStyle& style, String&& string)
3410{
3411#if !PLATFORM(COCOA)(defined 1 && 1)
3412 UNUSED_PARAM(style)(void)style;
3413 return WTF::move(string);
3414#else
3415 if (style.textSecurity() == TextSecurity::None)
3416 return WTF::move(string);
3417 // This PUA character in the system font is used to render password field dots on Cocoa platforms.
3418 constexpr char16_t textSecurityDiscPUACodePoint = 0xF79A;
3419 Ref font = style.fontCascade().primaryFont();
3420 if (!(font->platformData().isSystemFont() && font->glyphForCharacter(textSecurityDiscPUACodePoint)))
3421 return WTF::move(string);
3422
3423 // See RenderText::setRenderedText()
3424#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
3425 constexpr char16_t discCharacterToReplace = blackCircle;
3426#else
3427 constexpr char16_t discCharacterToReplace = bullet;
3428#endif
3429
3430 return makeStringByReplacingAll(string, discCharacterToReplace, textSecurityDiscPUACodePoint);
3431#endif
3432}
3433
3434LayoutUnit RenderBlock::layoutOverflowLogicalBottom(const RenderBlock& renderer)
3435{
3436 ASSERT(is<RenderGrid>(renderer) || is<RenderFlexibleBox>(renderer))((void)0);
3437 auto maxChildLogicalBottom = LayoutUnit { };
3438 for (auto& child : childrenOfType<RenderBox>(renderer)) {
3439 if (child.isOutOfFlowPositioned())
3440 continue;
3441 auto childLogicalBottom = renderer.logicalTopForChild(child) + renderer.logicalHeightForChild(child) + renderer.marginAfterForChild(child);
3442 maxChildLogicalBottom = std::max(maxChildLogicalBottom, childLogicalBottom);
3443 }
3444 return std::max(renderer.clientLogicalBottom(), maxChildLogicalBottom + renderer.paddingAfter());
3445}
3446
3447void RenderBlock::updateInFlowDescendantTransformsAfterLayout()
3448{
3449 auto boxes = view().frameView().layoutContext().takeBoxesNeedingTransformUpdateAfterContainerLayout(*this);
3450 for (auto& box : boxes) {
3451 if (box && box->hasLayer() && !box->isOutOfFlowPositioned())
3452 box->layer()->updateTransform();
3453 }
3454}
3455
3456void RenderBlock::updateOutOfFlowDescendantTransformsAfterLayout()
3457{
3458 auto boxes = view().frameView().layoutContext().takeBoxesNeedingTransformUpdateAfterContainerLayout(*this);
3459 for (auto& box : boxes) {
3460 if (box && box->hasLayer() && box->isOutOfFlowPositioned())
3461 box->layer()->updateTransform();
3462 }
3463}
3464
3465} // namespace WebCore