Bug Summary

File:Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/rendering/RenderReplaced.cpp
Warning:line 991, column 11
Local variable 'frameView' is uncounted and unsafe

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple arm64-apple-ios26.2.0-simulator -O3 -Wundef-prefix=TARGET_OS_ -Werror=undef-prefix -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -Werror=implicit-function-declaration -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name UnifiedSource416.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/UnifiedSource416.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/UnifiedSource416.plist -x c++ /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebCore/unified-sources/UnifiedSource416.cpp
1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2000 Dirk Mueller (mueller@kde.org)
4 * Copyright (C) 2004-2025 Apple Inc. All rights reserved.
5 * Copyright (C) 2018 Google Inc. All rights reserved.
6 * Copyright (C) Research In Motion Limited 2011-2012. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25#include "config.h"
26#include "RenderReplaced.h"
27
28#include "BackgroundPainter.h"
29#include "BorderShape.h"
30#include "DocumentMarkerController.h"
31#include "ElementRuleCollector.h"
32#include "FloatRoundedRect.h"
33#include "GraphicsContext.h"
34#include "HTMLElement.h"
35#include "HTMLImageElement.h"
36#include "HTMLParserIdioms.h"
37#include "HighlightRegistry.h"
38#include "InlineIteratorBox.h"
39#include "InlineIteratorLineBoxInlines.h"
40#include "LayoutRepainter.h"
41#include "LineSelection.h"
42#include "LocalFrame.h"
43#include "PositionedLayoutConstraints.h"
44#include "PaintInfoInlines.h"
45#include "RenderBlock.h"
46#include "RenderBoxInlines.h"
47#include "RenderChildIterator.h"
48#include "RenderElementStyleInlines.h"
49#include "RenderFlexibleBox.h"
50#include "RenderFragmentedFlow.h"
51#include "RenderHTMLCanvas.h"
52#include "RenderHighlight.h"
53#include "RenderImage.h"
54#include "RenderLayer.h"
55#include "RenderLayoutState.h"
56#include "RenderObjectInlines.h"
57#include "RenderStyle+GettersInlines.h"
58#include "RenderTheme.h"
59#include "RenderVideo.h"
60#include "RenderView.h"
61#include "RenderedDocumentMarker.h"
62#include "Settings.h"
63#include "StyleComputedStyle+InitialInlines.h"
64#include "StylePrimitiveNumericTypes+Evaluation.h"
65#include "VisiblePosition.h"
66#include <wtf/StackStats.h>
67#include <wtf/TZoneMallocInlines.h>
68#include <wtf/TypeCasts.h>
69
70namespace WebCore {
71
72WTF_MAKE_TZONE_ALLOCATED_IMPL(RenderReplaced)::bmalloc::api::HeapRef RenderReplaced::s_heapRef; const TZoneSpecification
RenderReplaced::s_heapSpec = { &RenderReplaced::s_heapRef
, TZoneSpecification::encodeSize<RenderReplaced>(), TZoneSpecification
::encodeAlignment<RenderReplaced>(), TZoneSpecification
::encodeCategory<RenderReplaced>(), ::bmalloc::api::compactAllocationMode
<RenderReplaced>(), TZoneSpecification::encodeDescriptor
<RenderReplaced>(), }; void* RenderReplaced::operatorNewSlow
(size_t size) { if constexpr (::bmalloc::api::compactAllocationMode
<RenderReplaced>() == CompactAllocationMode::Compact) return
::bmalloc::api::tzoneAllocateCompactSlow(size, s_heapSpec); return
::bmalloc::api::tzoneAllocateNonCompactSlow(size, s_heapSpec
); } using __makeBtzoneMallocedInlineMacroSemicolonifier __attribute__
((unused)) = int
;
73
74const int cDefaultWidth = 300;
75const int cDefaultHeight = 150;
76
77RenderReplaced::RenderReplaced(Type type, Element& element, RenderStyle&& style, OptionSet<ReplacedFlag> flags)
78 : RenderBox(type, element, WTF::move(style), { }, flags)
79 , m_intrinsicSize(cDefaultWidth, cDefaultHeight)
80{
81 ASSERT(element.isReplaced(&this->style()) || type == Type::Image)((void)0);
82 setBlockLevelReplacedOrAtomicInline(true);
83 ASSERT(isRenderReplaced())((void)0);
84}
85
86RenderReplaced::RenderReplaced(Type type, Element& element, RenderStyle&& style, const LayoutSize& intrinsicSize, OptionSet<ReplacedFlag> flags)
87 : RenderBox(type, element, WTF::move(style), { }, flags)
88 , m_intrinsicSize(intrinsicSize)
89{
90 ASSERT(element.isReplaced(&this->style()) || type == Type::Image)((void)0);
91 setBlockLevelReplacedOrAtomicInline(true);
92 ASSERT(isRenderReplaced())((void)0);
93}
94
95RenderReplaced::RenderReplaced(Type type, Document& document, RenderStyle&& style, const LayoutSize& intrinsicSize, OptionSet<ReplacedFlag> flags)
96 : RenderBox(type, document, WTF::move(style), { }, flags)
97 , m_intrinsicSize(intrinsicSize)
98{
99 setBlockLevelReplacedOrAtomicInline(true);
100 ASSERT(isRenderReplaced())((void)0);
101}
102
103RenderReplaced::~RenderReplaced() = default;
104
105bool RenderReplaced::shouldRespectZeroIntrinsicWidth() const
106{
107 // Per CSSWG resolution, 0px intrinsic width should be respected for SVG
108 // items and coerce the intrinsic height to 0px as well. Note that this is
109 // not the case for 0px intrinsic height SVGs or for other RenderReplaced items.
110 return false;
111}
112
113void RenderReplaced::willBeDestroyed()
114{
115 if (!renderTreeBeingDestroyed() && parent())
116 parent()->dirtyLineFromChangedChild();
117
118 RenderBox::willBeDestroyed();
119}
120
121void RenderReplaced::styleDidChange(Style::Difference diff, const RenderStyle* oldStyle)
122{
123 RenderBox::styleDidChange(diff, oldStyle);
124 auto previousUsedZoom = oldStyle ? oldStyle->usedZoom() : Style::evaluate<float>(Style::ComputedStyle::initialZoom());
125 if (previousUsedZoom != style().usedZoom())
126 intrinsicSizeChanged();
127}
128
129static bool shouldRepaintOnSizeChange(RenderReplaced& renderer)
130{
131 if (is<RenderHTMLCanvas>(renderer))
132 return true;
133
134#if ENABLE(VIDEO)(defined 1 && 1)
135 if (auto* renderImage = dynamicDowncast<RenderImage>(renderer); renderImage && !is<RenderMedia>(*renderImage) && !renderImage->isShowingMissingOrImageError())
136 return true;
137#endif
138
139 return false;
140}
141
142void RenderReplaced::layout()
143{
144 StackStats::LayoutCheckPoint layoutCheckPoint;
145 ASSERT(needsLayout())((void)0);
146
147 LayoutRepainter repainter(*this);
148
149 LayoutRect oldContentRect = replacedContentRect();
150
151 setHeight(minimumReplacedHeight());
152
153 updateLogicalWidth();
154 updateLogicalHeight();
155
156 clearOverflow();
157 addVisualEffectOverflow();
158 updateLayerTransform();
159 invalidateBackgroundObscurationStatus();
160 repainter.repaintAfterLayout();
161 clearNeedsLayout();
162
163 if (replacedContentRect() != oldContentRect) {
164 setNeedsPreferredWidthsUpdate();
165 if (shouldRepaintOnSizeChange(*this))
166 repaint();
167 }
168}
169
170void RenderReplaced::intrinsicSizeChanged()
171{
172 int scaledWidth = static_cast<int>(cDefaultWidth * style().usedZoom());
173 int scaledHeight = static_cast<int>(cDefaultHeight * style().usedZoom());
174 m_intrinsicSize = IntSize(scaledWidth, scaledHeight);
175 setNeedsLayoutAndPreferredWidthsUpdate();
176}
177
178bool RenderReplaced::shouldDrawSelectionTint() const
179{
180 return selectionState() != HighlightState::None && !document().printing();
181}
182
183inline static bool contentContainsReplacedElement(const Vector<WeakPtr<RenderedDocumentMarker>>& markers, const Element& element)
184{
185 for (auto& marker : markers) {
186 if (marker->type() == DocumentMarkerType::DraggedContent) {
187 if (std::get<RefPtr<Node>>(marker->data()) == &element)
188 return true;
189 } else if (marker->type() == DocumentMarkerType::TransparentContent) {
190 if (std::get<DocumentMarker::TransparentContentData>(marker->data()).node == &element)
191 return true;
192 }
193 }
194 return false;
195}
196
197Color RenderReplaced::calculateHighlightColor() const
198{
199 RenderHighlight renderHighlight;
200#if ENABLE(APP_HIGHLIGHTS)(defined 1 && 1)
201 if (auto appHighlightRegistry = document().appHighlightRegistryIfExists()) {
202 if (appHighlightRegistry->highlightsVisibility() == HighlightVisibility::Visible) {
203 for (auto& highlight : appHighlightRegistry->map()) {
204 for (auto& highlightRange : highlight.value->highlightRanges()) {
205 if (!renderHighlight.setRenderRange(highlightRange))
206 continue;
207
208 auto state = renderHighlight.highlightStateForRenderer(*this);
209 if (!isHighlighted(state, renderHighlight))
210 continue;
211
212 OptionSet<StyleColorOptions> styleColorOptions = { StyleColorOptions::UseSystemAppearance };
213 return theme().annotationHighlightBackgroundColor(styleColorOptions);
214 }
215 }
216 }
217 }
218#endif
219 if (auto highlightRegistry = document().highlightRegistryIfExists()) {
220 for (auto& highlight : highlightRegistry->map()) {
221 for (auto& highlightRange : highlight.value->highlightRanges()) {
222 if (!renderHighlight.setRenderRange(highlightRange))
223 continue;
224
225 auto state = renderHighlight.highlightStateForRenderer(*this);
226 if (!isHighlighted(state, renderHighlight))
227 continue;
228
229 if (auto highlightStyle = getCachedPseudoStyle({ PseudoElementType::Highlight, highlight.key }, &style()))
230 return highlightStyle->backgroundColorResolvingCurrentColor();
231 }
232 }
233 }
234
235 if (document().settings().scrollToTextFragmentEnabled()) {
236 if (auto highlightRegistry = document().fragmentHighlightRegistryIfExists()) {
237 for (auto& highlight : highlightRegistry->map()) {
238 for (auto& highlightRange : highlight.value->highlightRanges()) {
239 if (!renderHighlight.setRenderRange(highlightRange))
240 continue;
241
242 auto state = renderHighlight.highlightStateForRenderer(*this);
243 if (!isHighlighted(state, renderHighlight))
244 continue;
245
246 OptionSet<StyleColorOptions> styleColorOptions = { StyleColorOptions::UseSystemAppearance };
247 return theme().annotationHighlightBackgroundColor(styleColorOptions);
248 }
249 }
250 }
251 }
252 return Color();
253}
254
255void RenderReplaced::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
256{
257 if (!shouldPaint(paintInfo, paintOffset))
258 return;
259
260 LayoutPoint adjustedPaintOffset = paintOffset + location();
261
262 if (paintInfo.phase == PaintPhase::EventRegion) {
263 if (isRenderOrLegacyRenderSVGRoot() && !isSkippedContentRoot(*this))
264 paintReplaced(paintInfo, adjustedPaintOffset);
265 else if (visibleToHitTesting()) {
266 auto borderRect = LayoutRect(adjustedPaintOffset, size());
267 auto borderShape = BorderShape::shapeForBorderRect(style(), borderRect);
268 paintInfo.eventRegionContext()->unite(borderShape.deprecatedPixelSnappedRoundedRect(document().deviceScaleFactor()), *this, style());
269 }
270 return;
271 }
272
273 if (paintInfo.phase == PaintPhase::Accessibility) {
274 paintInfo.accessibilityRegionContext()->takeBounds(*this, adjustedPaintOffset);
275 return;
276 }
277
278 SetLayoutNeededForbiddenScope scope(*this);
279
280 GraphicsContextStateSaver savedGraphicsContext(paintInfo.context(), false);
281 if (element() && element()->parentOrShadowHostElement()) {
282 RefPtr parentContainer = element()->parentOrShadowHostElement();
283 ASSERT(parentContainer)((void)0);
284 CheckedPtr markers = document().markersIfExists();
285 if (markers) {
286 if (contentContainsReplacedElement(markers->markersFor(*parentContainer, DocumentMarkerType::DraggedContent), *element())) {
287 savedGraphicsContext.save();
288 paintInfo.context().setAlpha(0.25);
289 }
290 if (contentContainsReplacedElement(markers->markersFor(*parentContainer, DocumentMarkerType::TransparentContent), *element())) {
291 savedGraphicsContext.save();
292 paintInfo.context().setAlpha(0.0);
293 }
294 }
295 }
296
297 if (hasVisibleBoxDecorations() && paintInfo.phase == PaintPhase::Foreground)
298 paintBoxDecorations(paintInfo, adjustedPaintOffset);
299
300 if (paintInfo.phase == PaintPhase::Mask) {
301 paintMask(paintInfo, adjustedPaintOffset);
302 return;
303 }
304
305 if (paintInfo.phase == PaintPhase::ClippingMask && style().usedVisibility() == Visibility::Visible) {
306 paintClippingMask(paintInfo, adjustedPaintOffset);
307 return;
308 }
309
310 LayoutRect paintRect = LayoutRect(adjustedPaintOffset, size());
311 if (paintInfo.phase == PaintPhase::Outline || paintInfo.phase == PaintPhase::SelfOutline) {
312 if (style().usedOutlineWidth())
313 paintOutline(paintInfo, paintRect);
314 return;
315 }
316
317 if (paintInfo.phase != PaintPhase::Foreground && paintInfo.phase != PaintPhase::Selection)
318 return;
319
320 if (!paintInfo.shouldPaintWithinRoot(*this))
321 return;
322
323 Color highlightColor;
324 if (!document().printing() && !paintInfo.paintBehavior.contains(PaintBehavior::ExcludeSelection))
325 highlightColor = calculateHighlightColor();
326
327 bool drawSelectionTint = shouldDrawSelectionTint();
328 if (paintInfo.phase == PaintPhase::Selection) {
329 if (selectionState() == HighlightState::None)
330 return;
331 drawSelectionTint = false;
332 }
333
334 bool completelyClippedOut = false;
335 if (style().border().hasBorderRadius()) {
336 completelyClippedOut = size().isEmpty();
337 if (!completelyClippedOut) {
338 // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
339 paintInfo.context().save();
340 clipToContentBoxShape(paintInfo.context(), adjustedPaintOffset, document().deviceScaleFactor());
341 }
342 }
343
344 if (!completelyClippedOut) {
345 if (!isSkippedContentRoot(*this))
346 paintReplaced(paintInfo, adjustedPaintOffset);
347
348 if (style().border().hasBorderRadius())
349 paintInfo.context().restore();
350 }
351
352 // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of
353 // surrounding content.
354 if (drawSelectionTint) {
355 LayoutRect selectionPaintingRect = localSelectionRect();
356 selectionPaintingRect.moveBy(adjustedPaintOffset);
357 paintInfo.context().fillRect(snappedIntRect(selectionPaintingRect), selectionBackgroundColor());
358 }
359
360 if (highlightColor.isVisible()) {
361 auto selectionPaintingRect = localSelectionRect(false);
362 selectionPaintingRect.moveBy(adjustedPaintOffset);
363 paintInfo.context().fillRect(snappedIntRect(selectionPaintingRect), highlightColor);
364 }
365}
366
367bool RenderReplaced::shouldPaint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
368{
369 if ((paintInfo.paintBehavior.contains(PaintBehavior::ExcludeSelection)) && isSelected())
370 return false;
371
372 if (paintInfo.paintBehavior.contains(PaintBehavior::ExcludeReplacedContentExceptForIFrames) && !isRenderIFrame())
373 return false;
374
375 if (paintInfo.phase != PaintPhase::Foreground
376 && paintInfo.phase != PaintPhase::Outline
377 && paintInfo.phase != PaintPhase::SelfOutline
378 && paintInfo.phase != PaintPhase::Selection
379 && paintInfo.phase != PaintPhase::Mask
380 && paintInfo.phase != PaintPhase::ClippingMask
381 && paintInfo.phase != PaintPhase::EventRegion
382 && paintInfo.phase != PaintPhase::Accessibility)
383 return false;
384
385 if (!paintInfo.shouldPaintWithinRoot(*this))
386 return false;
387
388 // if we're invisible or haven't received a layout yet, then just bail.
389 if (style().usedVisibility() != Visibility::Visible)
390 return false;
391
392 LayoutRect paintRect(visualOverflowRect());
393 paintRect.moveBy(paintOffset + location());
394
395 // Early exit if the element touches the edges.
396 LayoutUnit top = paintRect.y();
397 LayoutUnit bottom = paintRect.maxY();
398
399 LayoutRect localRepaintRect = paintInfo.rect;
400 if (paintRect.x() >= localRepaintRect.maxX() || paintRect.maxX() <= localRepaintRect.x())
401 return false;
402
403 if (top >= localRepaintRect.maxY() || bottom <= localRepaintRect.y())
404 return false;
405
406 return true;
407}
408
409bool RenderReplaced::hasReplacedLogicalHeight() const
410{
411 if (style().logicalHeight().isAuto())
412 return false;
413
414 if (style().logicalHeight().isFixed())
415 return true;
416
417 if (style().logicalHeight().isPercentOrCalculated())
418 return !hasAutoHeightOrContainingBlockWithAutoHeight();
419
420 if (style().logicalHeight().isIntrinsic())
421 return !style().aspectRatio().hasRatio();
422
423 return false;
424}
425
426bool RenderReplaced::setNeedsLayoutIfNeededAfterIntrinsicSizeChange()
427{
428 setNeedsPreferredWidthsUpdate();
429
430 // If the actual area occupied by the image has changed and it is not constrained by style then a layout is required.
431 bool imageSizeIsConstrained = style().logicalWidth().isSpecified() && style().logicalHeight().isSpecified()
432 && !style().logicalMinWidth().isIntrinsic() && !style().logicalMaxWidth().isIntrinsic()
433 && !hasAutoHeightOrContainingBlockWithAutoHeight(UpdatePercentageHeightDescendants::No);
434
435 // FIXME: We only need to recompute the containing block's preferred size
436 // if the containing block's size depends on the image's size (i.e., the container uses shrink-to-fit sizing).
437 // There's no easy way to detect that shrink-to-fit is needed, always force a layout.
438 bool containingBlockNeedsToRecomputePreferredSize =
439 style().logicalWidth().isPercentOrCalculated()
440 || style().logicalMaxWidth().isPercentOrCalculated()
441 || style().logicalMinWidth().isPercentOrCalculated();
442
443 // Flex and grid layout use the intrinsic image width/height even if width/height are specified.
444 if (!imageSizeIsConstrained || containingBlockNeedsToRecomputePreferredSize || isFlexItem() || isGridItem()) {
445 setNeedsLayout();
446 return true;
447 }
448
449 return false;
450}
451
452static bool isVideoWithDefaultObjectSize(const RenderReplaced* maybeVideo)
453{
454#if ENABLE(VIDEO)(defined 1 && 1)
455 if (auto* video = dynamicDowncast<RenderVideo>(maybeVideo))
456 return video->hasDefaultObjectSize();
457#else
458 UNUSED_PARAM(maybeVideo)(void)maybeVideo;
459#endif
460 return false;
461}
462
463void RenderReplaced::computeAspectRatioInformationForRenderBox(RenderBox* contentRenderer, FloatSize& constrainedSize, FloatSize& preferredAspectRatio) const
464{
465 FloatSize intrinsicSize;
466 if (shouldApplySizeOrInlineSizeContainment()) {
467 intrinsicSize = RenderReplaced::computeIntrinsicSize();
468 preferredAspectRatio = RenderReplaced::preferredAspectRatio();
469 } else if (contentRenderer) {
470 bool contentIsRenderReplaced = false;
471 if (auto* renderReplaced = dynamicDowncast<RenderReplaced>(contentRenderer)) {
472 intrinsicSize = renderReplaced->computeIntrinsicSize();
473 preferredAspectRatio = renderReplaced->preferredAspectRatio();
474 contentIsRenderReplaced = true;
475 }
476 if (style().aspectRatio().isRatio() || (style().aspectRatio().isAutoAndRatio() && preferredAspectRatio.isEmpty()))
477 preferredAspectRatio = FloatSize::narrowPrecision(style().aspectRatio().width().value, style().aspectRatio().height().value);
478
479 // Handle zoom & vertical writing modes here, as the embedded document doesn't know about them.
480 intrinsicSize.scale(style().usedZoom());
481
482 if (auto* image = dynamicDowncast<RenderImage>(*this))
483 intrinsicSize.scale(image->imageDevicePixelRatio());
484
485 // Update our intrinsic size to match what the content renderer has computed, so that when we
486 // constrain the size below, the correct intrinsic size will be obtained for comparison against
487 // min and max widths.
488 if (contentIsRenderReplaced && isRenderWidget()) {
489 // For RenderWidget content (e.g. <object> embedding an SVG document), always update
490 // m_intrinsicSize to reflect current intrinsic dimensions, applying CSS default fallback
491 // values (300x150) for dimensions with no intrinsic value (e.g. percentage-sized or
492 // removed attributes). This prevents stale cached sizes when attributes like width/height
493 // are removed. Note: this does not apply to RenderImage (<img src="svg">), which manages
494 // its own intrinsic size via setIntrinsicSize() during image load.
495 auto sizeToCache = intrinsicSize;
496 if (!sizeToCache.width())
497 sizeToCache.setWidth(cDefaultWidth);
498 if (!sizeToCache.height())
499 sizeToCache.setHeight(cDefaultHeight);
500 m_intrinsicSize = LayoutSize(sizeToCache);
501 } else if (!preferredAspectRatio.isEmpty() && !intrinsicSize.isZero())
502 m_intrinsicSize = LayoutSize(intrinsicSize);
503
504 if (!isHorizontalWritingMode()) {
505 if (!preferredAspectRatio.isEmpty())
506 preferredAspectRatio = preferredAspectRatio.transposedSize();
507 intrinsicSize = intrinsicSize.transposedSize();
508 }
509 } else {
510 intrinsicSize = computeIntrinsicSize();
511 preferredAspectRatio = this->preferredAspectRatio();
512 if (!preferredAspectRatio.isEmpty() && !intrinsicSize.isZero())
513 m_intrinsicSize = LayoutSize(isHorizontalWritingMode() ? intrinsicSize : intrinsicSize.transposedSize());
514 }
515 constrainedSize = intrinsicSize;
516}
517
518void RenderReplaced::computeIntrinsicSizesConstrainedByTransferredMinMaxSizes(RenderBox* contentRenderer, FloatSize& intrinsicSize, FloatSize& intrinsicRatio) const
519{
520 computeAspectRatioInformationForRenderBox(contentRenderer, intrinsicSize, intrinsicRatio);
521
522 // Now constrain the intrinsic size along each axis according to minimum and maximum width/heights along the
523 // opposite axis. So for example a maximum width that shrinks our width will result in the height we compute here
524 // having to shrink in order to preserve the aspect ratio. Because we compute these values independently along
525 // each axis, the final returned size may in fact not preserve the aspect ratio.
526 auto& style = this->style();
527 auto computedLogicalHeight = style.logicalHeight();
528 bool logicalHeightBehavesAsAuto = computedLogicalHeight.isAuto() || (computedLogicalHeight.isPercentOrCalculated() && !percentageLogicalHeightIsResolvable());
529 if (!intrinsicRatio.isZero() && style.logicalWidth().isAuto() && logicalHeightBehavesAsAuto) {
530 auto removeBorderAndPaddingFromMinMaxSizes = [](LayoutUnit& minSize, LayoutUnit &maxSize, LayoutUnit borderAndPadding) {
531 minSize = std::max(0_lu, minSize - borderAndPadding);
532 maxSize = std::max(0_lu, maxSize - borderAndPadding);
533 };
534
535 auto [minLogicalWidth, maxLogicalWidth] = computeMinMaxLogicalWidthFromAspectRatio();
536 removeBorderAndPaddingFromMinMaxSizes(minLogicalWidth, maxLogicalWidth, borderAndPaddingLogicalWidth());
537
538 auto [minLogicalHeight, maxLogicalHeight] = computeMinMaxLogicalHeightFromAspectRatio();
539 removeBorderAndPaddingFromMinMaxSizes(minLogicalHeight, maxLogicalHeight, borderAndPaddingLogicalHeight());
540
541 // Only apply min-constraints upward when the dimension is actually intrinsic (non-zero).
542 // Max-constraints can always be applied since they only shrink values.
543 LayoutUnit width { intrinsicSize.width() };
544 if (width > 0)
545 width = std::max(width, minLogicalWidth);
546 intrinsicSize.setWidth(std::min(width, maxLogicalWidth));
547
548 LayoutUnit height { intrinsicSize.height() };
549 if (height > 0)
550 height = std::max(height, minLogicalHeight);
551 intrinsicSize.setHeight(std::min(height, maxLogicalHeight));
552 }
553}
554
555LayoutRect RenderReplaced::replacedContentRect(const LayoutSize& intrinsicSize) const
556{
557 LayoutRect contentRect = contentBoxRect();
558 if (intrinsicSize.isEmpty())
559 return contentRect;
560
561 auto objectFit = style().objectFit();
562
563 LayoutRect finalRect = contentRect;
564 switch (objectFit) {
565 case ObjectFit::Contain:
566 case ObjectFit::ScaleDown:
567 case ObjectFit::Cover:
568 finalRect.setSize(finalRect.size().fitToAspectRatio(intrinsicSize, objectFit == ObjectFit::Cover ? AspectRatioFitGrow : AspectRatioFitShrink));
569 if (objectFit != ObjectFit::ScaleDown || finalRect.width() <= intrinsicSize.width())
570 break;
571 [[fallthrough]];
572 case ObjectFit::None:
573 finalRect.setSize(intrinsicSize);
574 break;
575 case ObjectFit::Fill:
576 break;
577 }
578
579 auto& objectPosition = style().objectPosition();
580 auto zoom = style().usedZoomForLength();
581
582 auto xOffset = Style::evaluate<LayoutUnit>(objectPosition.x, contentRect.width() - finalRect.width(), zoom);
583 auto yOffset = Style::evaluate<LayoutUnit>(objectPosition.y, contentRect.height() - finalRect.height(), zoom);
584
585 finalRect.move(xOffset, yOffset);
586
587 return finalRect;
588}
589
590double RenderReplaced::computeIntrinsicAspectRatio() const
591{
592 FloatSize intrinsicRatio;
593 FloatSize intrinsicSize;
594 computeAspectRatioInformationForRenderBox(embeddedContentBox(), intrinsicSize, intrinsicRatio);
595 return intrinsicRatio.aspectRatioDouble();
596}
597
598FloatSize RenderReplaced::computeIntrinsicSize() const
599{
600 // If there's an embeddedContentBox() of a remote, referenced document available, this code-path should never be used.
601 ASSERT(!embeddedContentBox() || shouldApplySizeOrInlineSizeContainment())((void)0);
602 return { intrinsicLogicalWidth(), intrinsicLogicalHeight() };
603}
604
605FloatSize RenderReplaced::preferredAspectRatio() const
606{
607 // If there's an embeddedContentBox() of a remote, referenced document available, this code-path should never be used.
608 ASSERT(!embeddedContentBox() || shouldApplySizeOrInlineSizeContainment())((void)0);
609 auto intrinsicSize = FloatSize(intrinsicLogicalWidth(), intrinsicLogicalHeight());
610 FloatSize preferredAspectRatio;
611
612 if (style().aspectRatio().hasRatio()) {
613 preferredAspectRatio = FloatSize::narrowPrecision(style().aspectRatioLogicalWidth().value, style().aspectRatioLogicalHeight().value);
614 if (style().aspectRatio().isRatio() || isVideoWithDefaultObjectSize(this))
615 return preferredAspectRatio;
616 }
617 // Figure out if we need to compute an intrinsic ratio.
618 if (!RenderBox::hasIntrinsicAspectRatio() && !isRenderOrLegacyRenderSVGRoot())
619 return preferredAspectRatio;
620
621 // After supporting contain-intrinsic-size, the intrinsicSize of size containment is not always empty.
622 if (intrinsicSize.isEmpty() || shouldApplySizeContainment())
623 return preferredAspectRatio;
624
625 return intrinsicSize;
626}
627
628LayoutUnit RenderReplaced::computeConstrainedLogicalWidth() const
629{
630 // The aforementioned 'constraint equation' used for block-level, non-replaced
631 // elements in normal flow:
632 // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' +
633 // 'padding-right' + 'border-right-width' + 'margin-right' = width of
634 // containing block
635 // see https://www.w3.org/TR/CSS22/visudet.html#blockwidth
636 LayoutUnit logicalWidth;
637 if (isOutOfFlowPositioned()) {
638 // For absolutely positioned elements with both left and right set,
639 // the available width is constrained by those values.
640 auto& logicalLeft = style().logicalLeft();
641 auto& logicalRight = style().logicalRight();
642 auto containerWidth = containingBlock()->clientLogicalWidth();
643
644 if (!logicalLeft.isAuto() && !logicalRight.isAuto()) {
645 auto left = Style::evaluate<LayoutUnit>(logicalLeft, containerWidth, style().usedZoomForLength());
646 auto right = Style::evaluate<LayoutUnit>(logicalRight, containerWidth, style().usedZoomForLength());
647 logicalWidth = containerWidth - left - right;
648 } else
649 logicalWidth = containerWidth;
650 } else
651 logicalWidth = containingBlock()->contentBoxLogicalWidth();
652
653 // This solves above equation for 'width' (== logicalWidth).
654 auto marginStart = Style::evaluateMinimum<LayoutUnit>(style().marginStart(), logicalWidth, style().usedZoomForLength());
655 auto marginEnd = Style::evaluateMinimum<LayoutUnit>(style().marginEnd(), logicalWidth, style().usedZoomForLength());
656
657 return std::max(0_lu, (logicalWidth - (marginStart + marginEnd + borderLeft() + borderRight() + paddingLeft() + paddingRight())));
658}
659
660void RenderReplaced::computeAspectRatioAdjustedIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
661{
662 computeIntrinsicLogicalWidths(minLogicalWidth, maxLogicalWidth);
663
664 if (!hasIntrinsicAspectRatio())
665 return;
666
667 auto& style = this->style();
668 auto computedAspectRatio = computeIntrinsicAspectRatio();
669 auto computedIntrinsicLogicalWidth = minLogicalWidth;
670
671 auto resolveHeightForAspectRatio = [&](auto& length, bool canResolvePercentage) -> std::optional<LayoutUnit> {
672 if (auto fixedHeight = length.tryFixed())
673 return LayoutUnit { fixedHeight->resolveZoom(style.usedZoomForLength()) };
674
675 if (length.isPercentOrCalculated() && canResolvePercentage)
676 return computePercentageLogicalHeight(length, UpdatePercentageHeightDescendants::No);
677 return std::nullopt;
678 };
679
680 // Resolve height and apply aspect ratio if available
681 if (auto resolvedLogicalHeight = resolveHeightForAspectRatio(style.logicalHeight(), hasReplacedLogicalHeight()))
682 computedIntrinsicLogicalWidth = *resolvedLogicalHeight * computedAspectRatio;
683
684 // Apply max-height constraint
685 if (auto resolvedLogicalMaxHeight = resolveHeightForAspectRatio(style.logicalMaxHeight(), !replacedMaxLogicalHeightComputesAsNone()))
686 computedIntrinsicLogicalWidth = std::min(computedIntrinsicLogicalWidth, LayoutUnit { *resolvedLogicalMaxHeight * computedAspectRatio });
687
688 // Apply min-height constraint
689 if (auto resolvedLogicalMinHeight = resolveHeightForAspectRatio(style.logicalMinHeight(), !replacedMinLogicalHeightComputesAsNone()))
690 computedIntrinsicLogicalWidth = std::max(computedIntrinsicLogicalWidth, LayoutUnit { *resolvedLogicalMinHeight * computedAspectRatio });
691
692 minLogicalWidth = computedIntrinsicLogicalWidth;
693 maxLogicalWidth = minLogicalWidth;
694}
695
696static inline LayoutUnit resolveWidthForRatio(LayoutUnit borderAndPaddingLogicalHeight, LayoutUnit borderAndPaddingLogicalWidth, LayoutUnit logicalHeight, double aspectRatio, BoxSizing boxSizing)
697{
698 if (boxSizing == BoxSizing::BorderBox)
699 return LayoutUnit((logicalHeight + borderAndPaddingLogicalHeight) * aspectRatio) - borderAndPaddingLogicalWidth;
700 return LayoutUnit(logicalHeight * aspectRatio);
701}
702
703static inline bool hasIntrinsicSize(RenderBox*contentRenderer, bool hasIntrinsicWidth, bool hasIntrinsicHeight )
704{
705 if (hasIntrinsicWidth && hasIntrinsicHeight)
706 return true;
707 if (hasIntrinsicWidth || hasIntrinsicHeight)
708 return contentRenderer && contentRenderer->isRenderOrLegacyRenderSVGRoot();
709 return false;
710}
711
712LayoutUnit RenderReplaced::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const
713{
714 auto& style = this->style();
715 if (style.logicalWidth().isSpecified())
716 return computeReplacedLogicalWidthRespectingMinMaxWidth(computeReplacedLogicalWidthUsing(style.logicalWidth()), shouldComputePreferred);
717 if (style.logicalWidth().isIntrinsic())
718 return computeReplacedLogicalWidthRespectingMinMaxWidth(computeReplacedLogicalWidthUsing(style.logicalWidth()), shouldComputePreferred);
719
720 RenderBox* contentRenderer = embeddedContentBox();
721
722 // 10.3.2 Inline, replaced elements: http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width
723 FloatSize intrinsicRatio;
724 FloatSize constrainedSize;
725 computeIntrinsicSizesConstrainedByTransferredMinMaxSizes(contentRenderer, constrainedSize, intrinsicRatio);
726
727 if (style.logicalWidth().isAuto()) {
728 bool computedHeightIsAuto = style.logicalHeight().isAuto();
729 bool hasIntrinsicWidth = constrainedSize.width() > 0 || (!constrainedSize.width() && shouldRespectZeroIntrinsicWidth()) || shouldApplySizeOrInlineSizeContainment();
730 bool hasIntrinsicHeight = constrainedSize.height() > 0 || shouldApplySizeContainment();
731
732 // For flex or grid items where the logical height has been overriden then we should use that size to compute the replaced width as long as the flex or
733 // grid item has an intrinsic size. It is possible (indeed, common) for an SVG graphic to have an intrinsic aspect ratio but not to have an intrinsic
734 // width or height. There are also elements with intrinsic sizes but without intrinsic ratio (like an iframe).
735 if (auto overridingLogicalHeight = (!intrinsicRatio.isEmpty() && (isFlexItem() || isGridItem()) && hasIntrinsicSize(contentRenderer, hasIntrinsicWidth, hasIntrinsicHeight) ? this->overridingBorderBoxLogicalHeight() : std::nullopt))
736 return computeReplacedLogicalWidthRespectingMinMaxWidth(contentBoxLogicalHeight(*overridingLogicalHeight) * intrinsicRatio.aspectRatioDouble(), shouldComputePreferred);
737
738 // If 'height' and 'width' both have computed values of 'auto' and the element also has an intrinsic width, then that intrinsic width is the used value of 'width'.
739 if (computedHeightIsAuto && hasIntrinsicWidth)
740 return computeReplacedLogicalWidthRespectingMinMaxWidth(constrainedSize.width(), shouldComputePreferred);
741
742 if (!intrinsicRatio.isEmpty()) {
743 // If 'height' and 'width' both have computed values of 'auto' and the element has no intrinsic width, but does have an intrinsic height and intrinsic ratio;
744 // or if 'width' has a computed value of 'auto', 'height' has some other computed value, and the element does have an intrinsic ratio; then the used value
745 // of 'width' is: (used height) * (intrinsic ratio)
746 if (!computedHeightIsAuto || (!hasIntrinsicWidth && hasIntrinsicHeight)) {
747 auto estimatedUsedWidth = [&] {
748 if (hasIntrinsicWidth)
749 return LayoutUnit(constrainedSize.width());
750
751 if (shouldComputePreferred == ShouldComputePreferred::ComputePreferred)
752 return computeReplacedLogicalWidthRespectingMinMaxWidth(0_lu, ShouldComputePreferred::ComputePreferred);
753
754 auto constrainedLogicalWidth = computeConstrainedLogicalWidth();
755 return computeReplacedLogicalWidthRespectingMinMaxWidth(constrainedLogicalWidth, ShouldComputePreferred::ComputeActual);
756 }();
757
758 LayoutUnit logicalHeight = computeReplacedLogicalHeight(std::optional<LayoutUnit>(estimatedUsedWidth));
759 auto boxSizing = style.aspectRatio().hasRatio() ? style.boxSizingForAspectRatio() : BoxSizing::ContentBox;
760 return computeReplacedLogicalWidthRespectingMinMaxWidth(resolveWidthForRatio(borderAndPaddingLogicalHeight(), borderAndPaddingLogicalWidth(), logicalHeight, intrinsicRatio.aspectRatioDouble(), boxSizing), shouldComputePreferred);
761 }
762
763 // If 'height' and 'width' both have computed values of 'auto' and the
764 // element has an intrinsic ratio but no intrinsic height or width, then
765 // the used value of 'width' is undefined in CSS 2.1. However, it is
766 // suggested that, if the containing block's width does not itself depend
767 // on the replaced element's width, then the used value of 'width' is
768 // calculated from the constraint equation used for block-level,
769 // non-replaced elements in normal flow.
770 if (computedHeightIsAuto && !hasIntrinsicWidth && !hasIntrinsicHeight) {
771 bool isFlexItemComputingBaseSize = isFlexItem() && downcast<RenderFlexibleBox>(parent())->isComputingFlexBaseSizes();
772 if (shouldComputePreferred == ShouldComputePreferred::ComputePreferred && !isFlexItemComputingBaseSize)
773 return computeReplacedLogicalWidthRespectingMinMaxWidth(0_lu, ShouldComputePreferred::ComputePreferred);
774
775 auto constrainedLogicalWidth = computeConstrainedLogicalWidth();
776 auto [transferredMinLogicalWidth, transferredMaxLogicalWidth] = computeMinMaxLogicalWidthFromAspectRatio();
777 ASSERT(transferredMinLogicalWidth <= transferredMaxLogicalWidth)((void)0);
778 constrainedLogicalWidth = std::clamp(constrainedLogicalWidth, transferredMinLogicalWidth, transferredMaxLogicalWidth);
779
780 // For absolutely positioned elements with both insets set, don't let max-width intrinsic
781 // keywords constrain the width.
782 auto& style = this->style();
783 if (isOutOfFlowPositioned() && !style.logicalLeft().isAuto() && !style.logicalRight().isAuto()) {
784 // Still respect min-width, but ignore max-width if it's an intrinsic keyword.
785 auto& logicalMinWidth = style.logicalMinWidth();
786 auto minLogicalWidth = logicalMinWidth.isIntrinsic() ? 0_lu : computeReplacedLogicalWidthUsing(logicalMinWidth);
787 return std::max(minLogicalWidth, constrainedLogicalWidth);
788 }
789
790 return computeReplacedLogicalWidthRespectingMinMaxWidth(constrainedLogicalWidth, ShouldComputePreferred::ComputeActual);
791 }
792 }
793
794 // Otherwise, if 'width' has a computed value of 'auto', and the element has an intrinsic width, then that intrinsic width is the used value of 'width'.
795 if (hasIntrinsicWidth)
796 return computeReplacedLogicalWidthRespectingMinMaxWidth(constrainedSize.width(), shouldComputePreferred);
797
798 // Otherwise, if 'width' has a computed value of 'auto', but none of the conditions above are met, then the used value of 'width' becomes 300px. If 300px is too
799 // wide to fit the device, UAs should use the width of the largest rectangle that has a 2:1 ratio and fits the device instead.
800 // Note: We fall through and instead return intrinsicLogicalWidth() here - to preserve existing WebKit behavior, which might or might not be correct, or desired.
801 // Changing this to return cDefaultWidth, will affect lots of test results. Eg. some tests assume that a blank <img> tag (which implies width/height=auto)
802 // has no intrinsic size, which is wrong per CSS 2.1, but matches our behavior since a long time.
803 }
804
805 return computeReplacedLogicalWidthRespectingMinMaxWidth(intrinsicLogicalWidth(), shouldComputePreferred);
806}
807
808LayoutUnit RenderReplaced::computeReplacedLogicalHeight(std::optional<LayoutUnit> estimatedUsedWidth) const
809{
810 // 10.5 Content height: the 'height' property: http://www.w3.org/TR/CSS21/visudet.html#propdef-height
811 if (hasReplacedLogicalHeight())
812 return computeReplacedLogicalHeightRespectingMinMaxHeight(computeReplacedLogicalHeightUsing(style().logicalHeight()));
813
814 RenderBox* contentRenderer = embeddedContentBox();
815
816 // 10.6.2 Inline, replaced elements: http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-height
817 FloatSize intrinsicRatio;
818 FloatSize constrainedSize;
819 computeIntrinsicSizesConstrainedByTransferredMinMaxSizes(contentRenderer, constrainedSize, intrinsicRatio);
820
821 bool widthIsAuto = style().logicalWidth().isAuto();
822 bool hasIntrinsicWidth = constrainedSize.width() > 0 || (!constrainedSize.width() && shouldRespectZeroIntrinsicWidth()) || shouldApplySizeOrInlineSizeContainment();
823 bool hasIntrinsicHeight = constrainedSize.height() > 0 || shouldApplySizeContainment();
824
825 // See computeReplacedLogicalHeight() for a similar check for heights.
826 if (auto overridinglogicalWidth = (!intrinsicRatio.isEmpty() && (isFlexItem() || isGridItem()) && hasIntrinsicSize(contentRenderer, hasIntrinsicWidth, hasIntrinsicHeight) ? overridingBorderBoxLogicalWidth() : std::nullopt))
827 return computeReplacedLogicalHeightRespectingMinMaxHeight(contentBoxLogicalWidth(*overridinglogicalWidth) * intrinsicRatio.transposedSize().aspectRatioDouble());
828
829 // If 'height' and 'width' both have computed values of 'auto' and the element also has an intrinsic height, then that intrinsic height is the used value of 'height'.
830 if (widthIsAuto && hasIntrinsicHeight)
831 return computeReplacedLogicalHeightRespectingMinMaxHeight(constrainedSize.height());
832
833 // Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic ratio then the used value of 'height' is:
834 // (used width) / (intrinsic ratio)
835 if (!intrinsicRatio.isEmpty()) {
836 LayoutUnit usedWidth = estimatedUsedWidth ? estimatedUsedWidth.value() : contentBoxLogicalWidth();
837 BoxSizing boxSizing = BoxSizing::ContentBox;
838 if (style().aspectRatio().hasRatio())
839 boxSizing = style().boxSizingForAspectRatio();
840 return computeReplacedLogicalHeightRespectingMinMaxHeight(resolveHeightForRatio(borderAndPaddingLogicalWidth(), borderAndPaddingLogicalHeight(), usedWidth, intrinsicRatio.transposedSize().aspectRatioDouble(), boxSizing));
841 }
842
843 // Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic height, then that intrinsic height is the used value of 'height'.
844 if (hasIntrinsicHeight)
845 return computeReplacedLogicalHeightRespectingMinMaxHeight(constrainedSize.height());
846
847 // Otherwise, if 'height' has a computed value of 'auto', but none of the conditions above are met, then the used value of 'height' must be set to the height
848 // of the largest rectangle that has a 2:1 ratio, has a height not greater than 150px, and has a width not greater than the device width.
849 return computeReplacedLogicalHeightRespectingMinMaxHeight(intrinsicLogicalHeight());
850}
851
852void RenderReplaced::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
853{
854 minLogicalWidth = maxLogicalWidth = intrinsicLogicalWidth();
855}
856
857void RenderReplaced::computePreferredLogicalWidths()
858{
859 ASSERT(needsPreferredLogicalWidthsUpdate())((void)0);
860
861 // We cannot resolve any percent logical width here as the available logical
862 // width may not be set on our containing block.
863 if (style().logicalWidth().isPercentOrCalculated())
864 computeAspectRatioAdjustedIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
865 else
866 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeReplacedLogicalWidth(ShouldComputePreferred::ComputePreferred);
867
868 bool ignoreMinMaxSizes = shouldIgnoreLogicalMinMaxWidthSizes();
869 const RenderStyle& styleToUse = style();
870 if (styleToUse.logicalWidth().isPercentOrCalculated() || styleToUse.logicalMaxWidth().isPercentOrCalculated())
871 m_minPreferredLogicalWidth = 0;
872
873 if (auto fixedLogicalMinWidth = styleToUse.logicalMinWidth().tryFixed(); !ignoreMinMaxSizes && fixedLogicalMinWidth && fixedLogicalMinWidth->isPositive()) {
874 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(*fixedLogicalMinWidth));
875 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(*fixedLogicalMinWidth));
876 }
877
878 if (auto fixedLogicalMaxWidth = styleToUse.logicalMaxWidth().tryFixed(); !ignoreMinMaxSizes && fixedLogicalMaxWidth) {
879 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(*fixedLogicalMaxWidth));
880 m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(*fixedLogicalMaxWidth));
881 }
882
883 LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth();
884 m_minPreferredLogicalWidth += borderAndPadding;
885 m_maxPreferredLogicalWidth += borderAndPadding;
886
887 clearNeedsPreferredWidthsUpdate();
888}
889
890PositionWithAffinity RenderReplaced::positionForPoint(const LayoutPoint& point, HitTestSource source, const RenderFragmentContainer* fragment)
891{
892 auto [top, bottom] = [&]() -> std::pair<float, float> {
893 if (auto run = InlineIterator::boxFor(*this)) {
894 auto lineBox = run->lineBox();
895 auto lineContentTop = LayoutUnit { std::min(previousLineBoxContentBottomOrBorderAndPadding(*lineBox), lineBox->contentLogicalTop()) };
896 return std::make_pair(lineContentTop, LineSelection::logicalBottom(*lineBox));
897 }
898 return std::make_pair(logicalTop(), logicalBottom());
899 }();
900
901 LayoutUnit blockDirectionPosition = isHorizontalWritingMode() ? point.y() + y() : point.x() + x();
902 LayoutUnit lineDirectionPosition = isHorizontalWritingMode() ? point.x() + x() : point.y() + y();
903
904 if (blockDirectionPosition < top)
905 return createPositionWithAffinity(caretMinOffset(), Affinity::Downstream); // coordinates are above
906
907 if (blockDirectionPosition >= bottom)
908 return createPositionWithAffinity(caretMaxOffset(), Affinity::Downstream); // coordinates are below
909
910 if (element()) {
911 if (lineDirectionPosition <= logicalLeft() + (logicalWidth() / 2))
912 return createPositionWithAffinity(0, Affinity::Downstream);
913 return createPositionWithAffinity(1, Affinity::Downstream);
914 }
915
916 return RenderBox::positionForPoint(point, source, fragment);
917}
918
919LayoutRect RenderReplaced::selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool clipToVisibleContent)
920{
921 ASSERT(!needsLayout())((void)0);
922
923 if (!isSelected())
924 return LayoutRect();
925
926 LayoutRect rect = localSelectionRect();
927 if (clipToVisibleContent)
928 return computeRectForRepaint(rect, repaintContainer);
929 return localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
930}
931
932LayoutRect RenderReplaced::localSelectionRect(bool checkWhetherSelected) const
933{
934 if (checkWhetherSelected && !isSelected())
935 return LayoutRect();
936
937 return LayoutRect(LayoutPoint(), size());
938}
939
940bool RenderReplaced::isSelected() const
941{
942 return isHighlighted(selectionState(), view().selection());
943}
944
945bool RenderReplaced::isHighlighted(HighlightState state, const RenderHighlight& rangeData) const
946{
947 if (state == HighlightState::None)
948 return false;
949 if (state == HighlightState::Inside)
950 return true;
951
952 auto selectionStart = rangeData.startOffset();
953 auto selectionEnd = rangeData.endOffset();
954 if (state == HighlightState::Start)
955 return !selectionStart;
956
957 unsigned end = element()->hasChildNodes() ? element()->countChildNodes() : 1;
958 if (state == HighlightState::End)
959 return selectionEnd == end;
960 if (state == HighlightState::Both)
961 return !selectionStart && selectionEnd == end;
962 ASSERT_NOT_REACHED()((void)0);
963 return false;
964}
965
966auto RenderReplaced::localRectsForRepaint(RepaintOutlineBounds repaintOutlineBounds) const -> RepaintRects
967{
968 if (isInsideEntirelyHiddenLayer())
969 return { };
970
971 // The selectionRect can project outside of the overflowRect, so take their union
972 // for repainting to avoid selection painting glitches.
973 auto overflowRect = unionRect(localSelectionRect(false), visualOverflowRect());
974
975 // FIXME: layoutDelta needs to be applied in parts before/after transforms and
976 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
977 overflowRect.move(view().frameView().layoutContext().layoutDelta());
978
979 auto rects = RepaintRects { overflowRect };
980 if (repaintOutlineBounds == RepaintOutlineBounds::Yes)
981 rects.outlineBoundsRect = localOutlineBoundsRepaintRect();
982
983 return rects;
984}
985
986bool RenderReplaced::isContentLikelyVisibleInViewport()
987{
988 if (!isVisibleIgnoringGeometry())
989 return false;
990
991 auto& frameView = view().frameView();
Local variable 'frameView' is uncounted and unsafe
992 auto visibleRect = LayoutRect(frameView.windowToContents(frameView.windowClipRect()));
993 auto contentRect = computeRectForRepaint(replacedContentRect(), nullptr);
994
995 // Content rectangle may be empty because it is intrinsically sized and the content has not loaded yet.
996 if (contentRect.isEmpty() && (style().logicalWidth().isAuto() || style().logicalHeight().isAuto()))
997 return visibleRect.contains(contentRect.location());
998
999 return visibleRect.intersects(contentRect);
1000}
1001
1002bool RenderReplaced::shouldInvalidatePreferredWidths() const
1003{
1004 // If the height is a percentage and the width is auto, then the containingBlocks's height changing can cause this node to change it's preferred width because it maintains aspect ratio.
1005 return (hasRelativeLogicalHeight() || (isGridItem() && hasStretchedLogicalHeight())) && style().logicalWidth().isAuto();
1006}
1007
1008LayoutSize RenderReplaced::intrinsicSize() const
1009{
1010 if (!view().frameView().layoutContext().isInRenderTreeLayout()) {
1011 // 'contain' removes the natural aspect ratio / width / height only for the purposes of sizing and layout of the box.
1012 return m_intrinsicSize;
1013 }
1014
1015 auto size = m_intrinsicSize;
1016 auto zoomValue = style().usedZoom();
1017 if (isHorizontalWritingMode() ? shouldApplySizeOrInlineSizeContainment() : shouldApplySizeContainment())
1018 size.setWidth(explicitIntrinsicInnerWidth().value_or(0_lu) * zoomValue);
1019 if (isHorizontalWritingMode() ? shouldApplySizeContainment() : shouldApplySizeOrInlineSizeContainment())
1020 size.setHeight(explicitIntrinsicInnerHeight().value_or(0_lu) * zoomValue);
1021 return size;
1022}
1023
1024void RenderReplaced::layoutShadowContent(const LayoutSize& oldSize)
1025{
1026 for (auto& renderBox : childrenOfType<RenderBox>(*this)) {
1027 auto newSize = contentBoxRect().size();
1028
1029 if (is<RenderImage>(this)) {
1030 bool childNeedsLayout = renderBox.needsLayout();
1031 // If the region chain has changed we also need to relayout the children to update the region box info.
1032 // FIXME: We can do better once we compute region box info for RenderReplaced, not only for RenderBlock.
1033 if (CheckedPtr fragmentedFlow = enclosingFragmentedFlow(); fragmentedFlow && !childNeedsLayout) {
1034 if (fragmentedFlow->pageLogicalSizeChanged())
1035 childNeedsLayout = true;
1036 }
1037
1038 if (newSize == oldSize && !childNeedsLayout)
1039 continue;
1040 }
1041
1042 // When calling layout() on a child node, a parent must either push a LayoutStateMaintainer, or
1043 // instantiate LayoutStateDisabler. Since using a LayoutStateMaintainer is slightly more efficient,
1044 // and this method might be called many times per second during video playback, use a LayoutStateMaintainer:
1045 LayoutStateMaintainer statePusher(*this, locationOffset(), isTransformed() || hasReflection() || writingMode().isBlockFlipped());
1046 renderBox.setLocation(LayoutPoint(borderLeft(), borderTop()) + LayoutSize(paddingLeft(), paddingTop()));
1047
1048 auto usedZoom = renderBox.style().usedZoomForLength();
1049 renderBox.mutableStyle().setHeight(Style::PreferredSize::Fixed { newSize.height() / usedZoom.value });
1050 renderBox.mutableStyle().setWidth(Style::PreferredSize::Fixed { newSize.width() / usedZoom.value });
1051
1052 renderBox.setNeedsLayout(MarkOnlyThis);
1053 renderBox.layout();
1054 }
1055
1056 clearChildNeedsLayout();
1057}
1058
1059FloatSize RenderReplaced::intrinsicRatio() const
1060{
1061 FloatSize intrinsicRatio;
1062 FloatSize constrainedSize;
1063 computeAspectRatioInformationForRenderBox(embeddedContentBox(), constrainedSize, intrinsicRatio);
1064 return intrinsicRatio;
1065}
1066
1067void RenderReplaced::computeReplacedOutOfFlowPositionedLogicalWidth(LogicalExtentComputedValues& computedValues) const
1068{
1069 PositionedLayoutConstraints inlineConstraints(*this, LogicalBoxAxis::Inline);
1070 inlineConstraints.computeInsets();
1071
1072 // NOTE: This value of width is final in that the min/max width calculations
1073 // are dealt with in computeReplacedWidth(). This means that the steps to produce
1074 // correct max/min in the non-replaced version, are not necessary.
1075 computedValues.extent = computeReplacedLogicalWidth() + borderAndPaddingLogicalWidth();
1076
1077 inlineConstraints.resolvePosition(computedValues);
1078 inlineConstraints.fixupLogicalLeftPosition(computedValues);
1079}
1080
1081void RenderReplaced::computeReplacedOutOfFlowPositionedLogicalHeight(LogicalExtentComputedValues& computedValues) const
1082{
1083 PositionedLayoutConstraints blockConstraints(*this, LogicalBoxAxis::Block);
1084 blockConstraints.computeInsets();
1085
1086 // NOTE: This value of height is final in that the min/max height calculations
1087 // are dealt with in computeReplacedHeight(). This means that the steps to produce
1088 // correct max/min in the non-replaced version, are not necessary.
1089 computedValues.extent = computeReplacedLogicalHeight() + borderAndPaddingLogicalHeight();
1090
1091 blockConstraints.resolvePosition(computedValues);
1092 blockConstraints.adjustLogicalTopWithLogicalHeightIfNeeded(computedValues);
1093}
1094
1095LayoutUnit RenderReplaced::computeReplacedLogicalWidthRespectingMinMaxWidth(LayoutUnit logicalWidth, ShouldComputePreferred shouldComputePreferred) const
1096{
1097 if (shouldIgnoreLogicalMinMaxWidthSizes())
1098 return logicalWidth;
1099
1100 auto& logicalMinWidth = style().logicalMinWidth();
1101 auto& logicalMaxWidth = style().logicalMaxWidth();
1102 bool useLogicalWidthForMinWidth = (shouldComputePreferred == ShouldComputePreferred::ComputePreferred && logicalMinWidth.isPercentOrCalculated());
1103 bool useLogicalWidthForMaxWidth = (shouldComputePreferred == ShouldComputePreferred::ComputePreferred && logicalMaxWidth.isPercentOrCalculated()) || logicalMaxWidth.isNone();
1104 auto minLogicalWidth = useLogicalWidthForMinWidth ? logicalWidth : computeReplacedLogicalWidthUsing(logicalMinWidth);
1105 auto maxLogicalWidth = useLogicalWidthForMaxWidth ? logicalWidth : computeReplacedLogicalWidthUsing(logicalMaxWidth);
1106 return std::max(minLogicalWidth, std::min(logicalWidth, maxLogicalWidth));
1107}
1108
1109template<typename SizeType>
1110LayoutUnit RenderReplaced::computeReplacedLogicalWidthUsing(const SizeType& logicalWidth) const
1111{
1112 auto calculateContainerWidth = [&] {
1113 if (isOutOfFlowPositioned()) {
1114 PositionedLayoutConstraints constraints(*this, LogicalBoxAxis::Inline);
1115 return constraints.containingSize();
1116 }
1117 if (isHorizontalWritingMode() == containingBlock()->isHorizontalWritingMode())
1118 return containingBlockLogicalWidthForContent();
1119 return perpendicularContainingBlockLogicalHeight();
1120 };
1121
1122 auto percentageOrCalc = [&](Style::IsPercentageOrCalc auto const& logicalWidth) {
1123 // FIXME: Handle cases when containing block width is calculated or viewport percent.
1124 // https://bugs.webkit.org/show_bug.cgi?id=91071
1125 if (auto containerWidth = calculateContainerWidth(); containerWidth > 0 || (!containerWidth && (containingBlock()->style().logicalWidth().isSpecified()))) {
1126 if constexpr (Style::IsPercentage<std::decay_t<decltype(logicalWidth)>>)
1127 return adjustContentBoxLogicalWidthForBoxSizing(Style::evaluate<LayoutUnit>(logicalWidth, containerWidth));
1128 else
1129 return adjustContentBoxLogicalWidthForBoxSizing(Style::evaluate<LayoutUnit>(logicalWidth, containerWidth, style().usedZoomForLength()));
1130 }
1131 return 0_lu;
1132 };
1133
1134 auto content = [&](const auto& keyword, const auto& availableLogicalWidth) {
1135 // FIXME: Handle cases when containing block width is calculated or viewport percent.
1136 // https://bugs.webkit.org/show_bug.cgi?id=91071
1137 return computeIntrinsicLogicalWidthUsing(keyword, availableLogicalWidth, borderAndPaddingLogicalWidth()) - borderAndPaddingLogicalWidth();
1138 };
1139
1140 return WTF::switchOn(logicalWidth,
1141 [&](const typename SizeType::Fixed& fixedLogicalWidth) -> LayoutUnit {
1142 return adjustContentBoxLogicalWidthForBoxSizing(fixedLogicalWidth);
1143 },
1144 [&](const typename SizeType::Percentage& percentageLogicalWidth) -> LayoutUnit {
1145 return percentageOrCalc(percentageLogicalWidth);
1146 },
1147 [&](const typename SizeType::Calc& calculatedLogicalWidth) -> LayoutUnit {
1148 return percentageOrCalc(calculatedLogicalWidth);
1149 },
1150 [&](const CSS::Keyword::FitContent& keyword) -> LayoutUnit {
1151 return content(keyword, calculateContainerWidth());
1152 },
1153 [&](const CSS::Keyword::WebkitFillAvailable& keyword) -> LayoutUnit {
1154 return content(keyword, calculateContainerWidth());
1155 },
1156 [&](const CSS::Keyword::MinContent& keyword) -> LayoutUnit {
1157 // min-content/max-content don't need the availableLogicalWidth argument.
1158 return content(keyword, 0_lu);
1159 },
1160 [&](const CSS::Keyword::MaxContent& keyword) -> LayoutUnit {
1161 // min-content/max-content don't need the availableLogicalWidth argument.
1162 return content(keyword, 0_lu);
1163 },
1164 [&](const CSS::Keyword::Intrinsic&) -> LayoutUnit {
1165 return intrinsicLogicalWidth();
1166 },
1167 [&](const CSS::Keyword::MinIntrinsic&) -> LayoutUnit {
1168 return intrinsicLogicalWidth();
1169 },
1170 [&](const CSS::Keyword::Auto&) -> LayoutUnit {
1171 if constexpr (std::same_as<SizeType, Style::MinimumSize>)
1172 return 0_lu;
1173 else
1174 return intrinsicLogicalWidth();
1175 },
1176 [&](const CSS::Keyword::None&) -> LayoutUnit {
1177 return intrinsicLogicalWidth();
1178 }
1179 );
1180}
1181
1182bool RenderReplaced::replacedMinMaxLogicalHeightComputesAsNone(const auto& logicalHeight, const auto& initialLogicalHeight) const
1183{
1184 if (logicalHeight == initialLogicalHeight)
1185 return true;
1186
1187 if (isGridItem() && logicalHeight.isPercentOrCalculated()) {
1188 if (auto gridAreaContentLogicalHeight = this->gridAreaContentLogicalHeight())
1189 return !*gridAreaContentLogicalHeight;
1190 }
1191
1192 // Make sure % min-height and % max-height resolve to none if the containing block has auto height.
1193 // Note that the "height" case for replaced elements was handled by hasReplacedLogicalHeight, which is why
1194 // min and max-height are the only ones handled here.
1195 // FIXME: For now we put in a quirk for Apple Books until we can move them to viewport units.
1196#if PLATFORM(COCOA)(defined 1 && 1)
1197 // Allow min-max percentages in auto height blocks quirk.
1198 if (WTF::CocoaApplication::isAppleBooks())
1199 return false;
1200#endif
1201 if (CheckedPtr containingBlock = containingBlockForAutoHeightDetection(logicalHeight))
1202 return containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight();
1203 return false;
1204}
1205
1206bool RenderReplaced::replacedMinLogicalHeightComputesAsNone() const
1207{
1208 return replacedMinMaxLogicalHeightComputesAsNone(style().logicalMinHeight(), Style::ComputedStyle::initialMinHeight());
1209}
1210
1211bool RenderReplaced::replacedMaxLogicalHeightComputesAsNone() const
1212{
1213 return replacedMinMaxLogicalHeightComputesAsNone(style().logicalMaxHeight(), Style::ComputedStyle::initialMaxHeight());
1214}
1215
1216LayoutUnit RenderReplaced::computeReplacedLogicalHeightRespectingMinMaxHeight(LayoutUnit logicalHeight) const
1217{
1218 if (shouldIgnoreLogicalMinMaxHeightSizes())
1219 return logicalHeight;
1220
1221 LayoutUnit minLogicalHeight;
1222 if (!replacedMinLogicalHeightComputesAsNone())
1223 minLogicalHeight = computeReplacedLogicalHeightUsing(style().logicalMinHeight());
1224 LayoutUnit maxLogicalHeight = logicalHeight;
1225 if (!replacedMaxLogicalHeightComputesAsNone())
1226 maxLogicalHeight = computeReplacedLogicalHeightUsing(style().logicalMaxHeight());
1227 return std::max(minLogicalHeight, std::min(logicalHeight, maxLogicalHeight));
1228}
1229
1230template<typename SizeType>
1231LayoutUnit RenderReplaced::computeReplacedLogicalHeightUsingGeneric(const SizeType& logicalHeight) const
1232{
1233#if ASSERT_ENABLED0
1234 // This function should get called with Style::MinimumSize/Style::MaximumSize only if replaced[Min|Max]LogicalHeightComputesAsNone
1235 // returns false, otherwise we should not try to compute those values as they may be incorrect. The caller should make sure this
1236 // condition holds before calling this function
1237 if constexpr (std::same_as<SizeType, Style::MinimumSize>)
1238 ASSERT(!replacedMinLogicalHeightComputesAsNone())((void)0);
1239 else if constexpr (std::same_as<SizeType, Style::MaximumSize>)
1240 ASSERT(!replacedMaxLogicalHeightComputesAsNone())((void)0);
1241#endif
1242
1243 auto percentageOrCalculated = [&](Style::IsPercentageOrCalc auto const& logicalHeight) {
1244 auto* container = isOutOfFlowPositioned() ? this->container() : containingBlock();
1245 while (container && container->isAnonymousForPercentageResolution()) {
1246 // Stop at rendering context root.
1247 if (is<RenderView>(*container))
1248 break;
1249 container = container->containingBlock();
1250 }
1251 bool hasPerpendicularContainingBlock = container->isHorizontalWritingMode() != isHorizontalWritingMode();
1252 std::optional<LayoutUnit> stretchedHeight;
1253 if (auto* block = dynamicDowncast<RenderBlock>(container)) {
1254 block->addPercentHeightDescendant(*const_cast<RenderReplaced*>(this));
1255 if (auto usedFlexItemOverridingLogicalHeightForPercentageResolutionForFlex = (block->isFlexItem() ? downcast<RenderFlexibleBox>(block->parent())->usedFlexItemOverridingLogicalHeightForPercentageResolution(*block) : std::nullopt))
1256 stretchedHeight = block->contentBoxLogicalHeight(*usedFlexItemOverridingLogicalHeightForPercentageResolutionForFlex);
1257 else if (auto usedChildOverridingLogicalHeightForGrid = (block->isGridItem() && !hasPerpendicularContainingBlock ? block->overridingBorderBoxLogicalHeight() : std::nullopt))
1258 stretchedHeight = block->contentBoxLogicalHeight(*usedChildOverridingLogicalHeightForGrid);
1259 }
1260
1261 // FIXME: This calculation is not patched for block-flow yet.
1262 // https://bugs.webkit.org/show_bug.cgi?id=46500
1263 if (container->isOutOfFlowPositioned()
1264 && container->style().height().isAuto()
1265 && !(container->style().top().isAuto() || container->style().bottom().isAuto())) {
1266 auto& block = downcast<RenderBlock>(*container);
1267 auto computedValues = block.computeLogicalHeight(block.logicalHeight(), 0);
1268 LayoutUnit borderPaddingAdjustment = isOutOfFlowPositioned() ? block.borderLogicalHeight() : block.borderAndPaddingLogicalHeight();
1269 LayoutUnit newContentHeight = computedValues.extent - block.scrollbarLogicalHeight() - borderPaddingAdjustment;
1270
1271 if constexpr (Style::IsPercentage<std::decay_t<decltype(logicalHeight)>>)
1272 return adjustContentBoxLogicalHeightForBoxSizing(Style::evaluate<LayoutUnit>(logicalHeight, newContentHeight));
1273 else
1274 return adjustContentBoxLogicalHeightForBoxSizing(Style::evaluate<LayoutUnit>(logicalHeight, newContentHeight, style().usedZoomForLength()));
1275 }
1276
1277 LayoutUnit availableHeight;
1278 if (isOutOfFlowPositioned()) {
1279 PositionedLayoutConstraints constraints(*this, LogicalBoxAxis::Block);
1280 availableHeight = constraints.containingSize();
1281 } else if (stretchedHeight)
1282 availableHeight = stretchedHeight.value();
1283 else if (auto gridAreaLogicalHeight = isGridItem() ? this->gridAreaContentLogicalHeight() : std::nullopt; gridAreaLogicalHeight && *gridAreaLogicalHeight)
1284 availableHeight = gridAreaLogicalHeight->value();
1285 else {
1286 availableHeight = hasPerpendicularContainingBlock ? containingBlockLogicalWidthForContent() : containingBlockLogicalHeightForContent(AvailableLogicalHeightType::IncludeMarginBorderPadding);
1287 // It is necessary to use the border-box to match WinIE's broken
1288 // box model. This is essential for sizing inside
1289 // table cells using percentage heights.
1290 // FIXME: This needs to be made block-flow-aware. If the cell and image are perpendicular block-flows, this isn't right.
1291 // https://bugs.webkit.org/show_bug.cgi?id=46997
1292 while (container && !is<RenderView>(*container)
1293 && (container->style().logicalHeight().isAuto() || container->style().logicalHeight().isPercentOrCalculated())) {
1294 if (container->isRenderTableCell()) {
1295 // Don't let table cells squeeze percent-height replaced elements
1296 // <http://bugs.webkit.org/show_bug.cgi?id=15359>
1297 availableHeight = std::max(availableHeight, intrinsicLogicalHeight());
1298 if constexpr (Style::IsPercentage<std::decay_t<decltype(logicalHeight)>>)
1299 return Style::evaluate<LayoutUnit>(logicalHeight, availableHeight - borderAndPaddingLogicalHeight());
1300 else
1301 return Style::evaluate<LayoutUnit>(logicalHeight, availableHeight - borderAndPaddingLogicalHeight(), style().usedZoomForLength());
1302 }
1303 downcast<RenderBlock>(*container).addPercentHeightDescendant(const_cast<RenderReplaced&>(*this));
1304 container = container->containingBlock();
1305 }
1306 }
1307
1308 if constexpr (Style::IsPercentage<std::decay_t<decltype(logicalHeight)>>)
1309 return adjustContentBoxLogicalHeightForBoxSizing(Style::evaluate<LayoutUnit>(logicalHeight, availableHeight));
1310 else
1311 return adjustContentBoxLogicalHeightForBoxSizing(Style::evaluate<LayoutUnit>(logicalHeight, availableHeight, style().usedZoomForLength()));
1312 };
1313
1314 auto content = [&] {
1315 return adjustContentBoxLogicalHeightForBoxSizing(computeIntrinsicLogicalContentHeightUsing(logicalHeight, intrinsicLogicalHeight(), borderAndPaddingLogicalHeight()));
1316 };
1317
1318 return WTF::switchOn(logicalHeight,
1319 [&](const typename SizeType::Fixed& fixedLogicalHeight) -> LayoutUnit {
1320 return adjustContentBoxLogicalHeightForBoxSizing(LayoutUnit { fixedLogicalHeight.resolveZoom(style().usedZoomForLength()) });
1321 },
1322 [&](const typename SizeType::Percentage& percentageLogicalHeight) -> LayoutUnit {
1323 return percentageOrCalculated(percentageLogicalHeight);
1324 },
1325 [&](const typename SizeType::Calc& calculatedLogicalHeight) -> LayoutUnit {
1326 return percentageOrCalculated(calculatedLogicalHeight);
1327 },
1328 [&](const CSS::Keyword::FitContent&) -> LayoutUnit {
1329 auto [transferredMinLogicalHeight, transferredMaxLogicalHeight] = computeMinMaxLogicalHeightFromAspectRatio();
1330 return std::clamp(content(), transferredMinLogicalHeight, transferredMaxLogicalHeight);
1331 },
1332 [&](const CSS::Keyword::WebkitFillAvailable&) -> LayoutUnit {
1333 return content();
1334 },
1335 [&](const CSS::Keyword::MinContent&) -> LayoutUnit {
1336 auto [transferredMinLogicalHeight, transferredMaxLogicalHeight] = computeMinMaxLogicalHeightFromAspectRatio();
1337 return std::clamp(content(), transferredMinLogicalHeight, transferredMaxLogicalHeight);
1338 },
1339 [&](const CSS::Keyword::MaxContent&) -> LayoutUnit {
1340 auto [transferredMinLogicalHeight, transferredMaxLogicalHeight] = computeMinMaxLogicalHeightFromAspectRatio();
1341 return std::clamp(content(), transferredMinLogicalHeight, transferredMaxLogicalHeight);
1342 },
1343 [&](const CSS::Keyword::Intrinsic&) -> LayoutUnit {
1344 return intrinsicLogicalHeight();
1345 },
1346 [&](const CSS::Keyword::MinIntrinsic&) -> LayoutUnit {
1347 return intrinsicLogicalHeight();
1348 },
1349 [&](const CSS::Keyword::Auto&) -> LayoutUnit {
1350 if constexpr (std::same_as<SizeType, Style::MinimumSize>)
1351 return adjustContentBoxLogicalHeightForBoxSizing(LayoutUnit { 0 });
1352 else
1353 return intrinsicLogicalHeight();
1354 },
1355 [&](const CSS::Keyword::None&) -> LayoutUnit {
1356 return intrinsicLogicalHeight();
1357 }
1358 );
1359}
1360
1361LayoutUnit RenderReplaced::computeReplacedLogicalHeightUsing(const Style::PreferredSize& logicalHeight) const
1362{
1363 return computeReplacedLogicalHeightUsingGeneric(logicalHeight);
1364}
1365
1366LayoutUnit RenderReplaced::computeReplacedLogicalHeightUsing(const Style::MinimumSize& logicalHeight) const
1367{
1368 return computeReplacedLogicalHeightUsingGeneric(logicalHeight);
1369}
1370
1371LayoutUnit RenderReplaced::computeReplacedLogicalHeightUsing(const Style::MaximumSize& logicalHeight) const
1372{
1373 return computeReplacedLogicalHeightUsingGeneric(logicalHeight);
1374}
1375
1376}