Bug Summary

File:Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/rendering/BackgroundPainter.cpp
Warning:line 558, column 27
Local variable 'image' 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 UnifiedSource403.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/UnifiedSource403.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/UnifiedSource403.plist -x c++ /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebCore/unified-sources/UnifiedSource403.cpp
1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
5 * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
6 * Copyright (C) 2005-2025 Apple Inc. All rights reserved.
7 * Copyright (C) 2010-2013 Google Inc. All rights reserved.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 *
24 */
25
26#include "config.h"
27#include "BackgroundPainter.h"
28
29#include "BorderPainter.h"
30#include "BorderShape.h"
31#include "CachedImage.h"
32#include "ColorBlending.h"
33#include "ContainerNodeInlines.h"
34#include "FloatRoundedRect.h"
35#include "GeometryUtilities.h"
36#include "GraphicsContext.h"
37#include "InlineIteratorInlineBox.h"
38#include "PaintInfo.h"
39#include "RenderBoxInlines.h"
40#include "RenderBoxModelObjectInlines.h"
41#include "RenderElementStyleInlines.h"
42#include "RenderImage.h"
43#include "RenderLayer.h"
44#include "RenderLayerBacking.h"
45#include "RenderObjectInlines.h"
46#include "RenderTableCell.h"
47#include "RenderView.h"
48#include "Settings.h"
49#include "StyleBoxShadow.h"
50#include "StylePrimitiveNumericTypes+Evaluation.h"
51#include "TextBoxPainter.h"
52
53namespace WebCore {
54
55BackgroundImageGeometry::BackgroundImageGeometry(const LayoutRect& destinationRect, const LayoutSize& tileSizeWithoutPixelSnapping, const LayoutSize& tileSize, const LayoutSize& phase, const LayoutSize& spaceSize, bool fixedAttachment)
56 : destinationRect(destinationRect)
57 , destinationOrigin(destinationRect.location())
58 , tileSizeWithoutPixelSnapping(tileSizeWithoutPixelSnapping)
59 , tileSize(tileSize)
60 , phase(phase)
61 , spaceSize(spaceSize)
62 , hasNonLocalGeometry(fixedAttachment)
63{
64}
65
66BackgroundPainter::BackgroundPainter(RenderBoxModelObject& renderer, const PaintInfo& paintInfo)
67 : m_renderer(renderer)
68 , m_paintInfo(paintInfo)
69{
70 // background-clip has no effect when painting the root background.
71 // https://www.w3.org/TR/css-backgrounds-3/#background-clip
72 if (m_renderer.isDocumentElementRenderer())
73 setOverrideClip(FillBox::BorderBox);
74}
75
76void BackgroundPainter::paintBackground(const LayoutRect& paintRect, BleedAvoidance bleedAvoidance) const
77{
78 if (m_renderer.isDocumentElementRenderer()) {
79 paintRootBoxFillLayers();
80 return;
81 }
82
83 if (!paintsOwnBackground(m_renderer))
84 return;
85
86 if (auto* renderBox = dynamicDowncast<RenderBox>(m_renderer)) {
87 if (renderBox->backgroundIsKnownToBeObscured(paintRect.location()) && !boxShadowShouldBeAppliedToBackground(*renderBox, paintRect.location(), bleedAvoidance, { }))
88 return;
89 }
90
91 auto backgroundColor = m_renderer.style().visitedDependentBackgroundColorApplyingColorFilter();
92 auto compositeOp = document().compositeOperatorForBackgroundColor(backgroundColor, m_renderer);
93
94 paintFillLayers(backgroundColor, m_renderer.style().backgroundLayers(), m_renderer.style().usedZoomForLength(), paintRect, bleedAvoidance, compositeOp);
95}
96
97void BackgroundPainter::paintRootBoxFillLayers() const
98{
99 ASSERT(m_renderer.isDocumentElementRenderer())((void)0);
100 if (m_paintInfo.skipRootBackground())
101 return;
102
103 auto* rootBackgroundRenderer = view().rendererForRootBackground();
104 if (!rootBackgroundRenderer)
105 return;
106
107 auto& style = rootBackgroundRenderer->style();
108 auto backgroundColor = style.visitedDependentBackgroundColorApplyingColorFilter();
109 auto compositeOp = document().compositeOperatorForBackgroundColor(backgroundColor, m_renderer);
110
111 paintFillLayers(backgroundColor, style.backgroundLayers(), style.usedZoomForLength(), view().backgroundRect(), BleedAvoidance::None, compositeOp, rootBackgroundRenderer);
112}
113
114bool BackgroundPainter::paintsOwnBackground(const RenderBoxModelObject& renderer)
115{
116 if (!renderer.isBody())
117 return true;
118 if (renderer.shouldApplyAnyContainment())
119 return true;
120 // The <body> only paints its background if the root element has defined a background independent of the body,
121 // or if the <body>'s parent is not the document element's renderer (e.g. inside SVG foreignObject).
122 auto documentElementRenderer = renderer.document().documentElement()->renderer();
123 return !documentElementRenderer || documentElementRenderer->shouldApplyAnyContainment() || documentElementRenderer->hasBackground() || documentElementRenderer != renderer.parent();
124}
125
126void BackgroundPainter::paintFillLayers(const Color& color, const Style::BackgroundLayers& fillLayerList, Style::ZoomFactor zoom, const LayoutRect& rect, BleedAvoidance bleedAvoidance, CompositeOperator op, RenderElement* backgroundObject) const
127{
128 paintFillLayersImpl(color, fillLayerList, zoom, rect, bleedAvoidance, op, backgroundObject);
129}
130
131void BackgroundPainter::paintFillLayers(const Color& color, const Style::MaskLayers& fillLayerList, Style::ZoomFactor zoom, const LayoutRect& rect, BleedAvoidance bleedAvoidance, CompositeOperator op, RenderElement* backgroundObject) const
132{
133 paintFillLayersImpl(color, fillLayerList, zoom, rect, bleedAvoidance, op, backgroundObject);
134}
135
136template<typename Layers> void BackgroundPainter::paintFillLayersImpl(const Color& color, const Layers& fillLayers, Style::ZoomFactor zoom, const LayoutRect& rect, BleedAvoidance bleedAvoidance, CompositeOperator op, RenderElement* backgroundObject) const
137{
138 bool shouldDrawBackgroundInSeparateBuffer = false;
139
140 Style::computeClipMax(fillLayers);
141
142 for (auto& layer : fillLayers.usedValues()) {
143 if (layer.blendMode() != BlendMode::Normal)
144 shouldDrawBackgroundInSeparateBuffer = true;
145
146 // Stop traversal when an opaque layer is encountered.
147 // FIXME: It would be possible for the following occlusion culling test to be more aggressive
148 // on layers with no repeat by testing whether the image covers the layout rect.
149 // Testing that here would imply duplicating a lot of calculations that are currently done in
150 // BackgroundPainter::paintFillLayer. A more efficient solution might be to move
151 // the layer recursion into paintFillLayer, or to compute the layer geometry here
152 // and pass it down.
153 if (layer.clipOccludesNextLayers()
154 && layer.hasOpaqueImage(m_renderer)
155 && layer.image().tryStyleImage()->canRender(&m_renderer, m_renderer.style().usedZoom())
156 && layer.hasRepeatXY()
157 && layer.blendMode() == BlendMode::Normal
158 && !boxShadowShouldBeAppliedToBackground(m_renderer, rect.location(), bleedAvoidance, { }))
159 break;
160 }
161
162 auto& context = m_paintInfo.context();
163 auto baseBgColorUsage = BaseBackgroundColorUse;
164
165 if (shouldDrawBackgroundInSeparateBuffer) {
166 paintFillLayerImpl(color, FillLayerToPaint<typename Layers::value_type> { .layer = fillLayers.usedLast(), .isLast = true, .zoom = zoom }, rect, bleedAvoidance, { }, { }, op, backgroundObject, BaseBackgroundColorOnly);
167 baseBgColorUsage = BaseBackgroundColorSkip;
168 context.beginTransparencyLayer(1);
169 }
170
171 for (auto& layer : fillLayers.usedValues() | std::views::reverse)
172 paintFillLayerImpl(color, FillLayerToPaint<typename Layers::value_type> { .layer = layer, .isLast = &layer == &fillLayers.usedLast(), .zoom = zoom }, rect, bleedAvoidance, { }, { }, op, backgroundObject, baseBgColorUsage);
173
174 if (shouldDrawBackgroundInSeparateBuffer)
175 context.endTransparencyLayer();
176}
177
178static void applyBoxShadowForBackground(GraphicsContext& context, const RenderStyle& style)
179{
180 Style::ColorResolver colorResolver { style };
181 const auto& zoomFactor = style.usedZoomForLength();
182
183 for (const auto& shadow : style.boxShadow()) {
184 if (shadow.inset)
185 continue;
186
187 context.setDropShadow({
188 FloatSize {
189 shadow.location.x().resolveZoom(zoomFactor),
190 shadow.location.y().resolveZoom(zoomFactor),
191 },
192 shadow.blur.resolveZoom(zoomFactor),
193 colorResolver.colorResolvingCurrentColorApplyingColorFilter(shadow.color),
194 shadow.isWebkitBoxShadow ? ShadowRadiusMode::Legacy : ShadowRadiusMode::Default
195 });
196 break;
197 }
198}
199
200void BackgroundPainter::paintFillLayer(const Color& color, const FillLayerToPaint<Style::BackgroundLayer>& layer, const LayoutRect& rect, BleedAvoidance bleedAvoidance, const InlineIterator::InlineBoxIterator& inlineBoxIterator, const LayoutRect& backgroundImageStrip, CompositeOperator op, RenderElement* backgroundObject, BaseBackgroundColorUsage baseBgColorUsage) const
201{
202 paintFillLayerImpl(color, layer, rect, bleedAvoidance, inlineBoxIterator, backgroundImageStrip, op, backgroundObject, baseBgColorUsage);
203}
204
205void BackgroundPainter::paintFillLayer(const Color& color, const FillLayerToPaint<Style::MaskLayer>& layer, const LayoutRect& rect, BleedAvoidance bleedAvoidance, const InlineIterator::InlineBoxIterator& inlineBoxIterator, const LayoutRect& backgroundImageStrip, CompositeOperator op, RenderElement* backgroundObject, BaseBackgroundColorUsage baseBgColorUsage) const
206{
207 paintFillLayerImpl(color, layer, rect, bleedAvoidance, inlineBoxIterator, backgroundImageStrip, op, backgroundObject, baseBgColorUsage);
208}
209
210template<typename Layer> void BackgroundPainter::paintFillLayerImpl(const Color& color, const FillLayerToPaint<Layer>& layer, const LayoutRect& rect, BleedAvoidance bleedAvoidance, const InlineIterator::InlineBoxIterator& inlineBoxIterator, const LayoutRect& backgroundImageStrip, CompositeOperator op, RenderElement* backgroundObject, BaseBackgroundColorUsage baseBgColorUsage) const
211{
212 GraphicsContext& context = m_paintInfo.context();
213
214 if ((context.paintingDisabled() && !context.detectingContentfulPaint()) || rect.isEmpty())
215 return;
216
217 auto closedEdges = inlineBoxIterator
218 ? inlineBoxIterator->closedEdges()
219 : RectEdges<bool> { true };
220
221 auto& style = m_renderer.style();
222 auto layerClip = m_overrideClip.value_or(layer.layer.clip());
223
224 bool hasRoundedBorder = style.border().hasBorderRadius()
225 && (closedEdges.start(style.writingMode()) || closedEdges.end(style.writingMode()));
226 bool clippedWithLocalScrolling = m_renderer.hasNonVisibleOverflow() && layer.layer.attachment() == FillAttachment::LocalBackground;
227 bool isBorderFill = layerClip == FillBox::BorderBox;
228 bool isRoot = m_renderer.isDocumentElementRenderer();
229
230 Color bgColor = color;
231 RefPtr bgImage = layer.layer.image().tryStyleImage();
232 bool shouldPaintBackgroundImage = bgImage && bgImage->canRender(&m_renderer, style.usedZoom());
233
234 if (context.detectingContentfulPaint()) {
235 if (!context.contentfulPaintDetected() && shouldPaintBackgroundImage && bgImage->cachedImage()) {
236 if (style.backgroundLayers().usedFirst().size().isEmpty())
237 context.setContentfulPaintDetected();
238 return;
239 }
240 }
241
242 if (context.invalidatingImagesWithAsyncDecodes()) {
243 if (shouldPaintBackgroundImage && bgImage->cachedImage()->isClientWaitingForAsyncDecoding(m_renderer.cachedImageClient()))
244 bgImage->cachedImage()->removeAllClientsWaitingForAsyncDecoding();
245 return;
246 }
247
248 bool forceBackgroundToWhite = false;
249 if (document().printing()) {
250 if (style.printColorAdjust() == PrintColorAdjust::Economy)
251 forceBackgroundToWhite = true;
252
253 if (document().settings().shouldPrintBackgrounds())
254 forceBackgroundToWhite = false;
255 }
256
257 // When printing backgrounds is disabled or using economy mode,
258 // change existing background colors and images to a solid white background.
259 // If there's no bg color or image, leave it untouched to avoid affecting transparency.
260 // We don't try to avoid loading the background images, because this style flag is only set
261 // when printing, and at that point we've already loaded the background images anyway. (To avoid
262 // loading the background images we'd have to do this check when applying styles rather than
263 // while rendering.)
264 if (forceBackgroundToWhite) {
265 // Note that we can't reuse this variable below because the bgColor might be changed
266 bool shouldPaintBackgroundColor = layer.isLast && bgColor.isVisible();
267 if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) {
268 bgColor = Color::white;
269 shouldPaintBackgroundImage = false;
270 }
271
272 if (layerClip == FillBox::Text)
273 layerClip = FillBox::BorderBox;
274 }
275
276 bool baseBgColorOnly = (baseBgColorUsage == BaseBackgroundColorOnly);
277 if (baseBgColorOnly && (!isRoot || !layer.isLast || bgColor.isOpaque()))
278 return;
279
280 bool colorVisible = bgColor.isVisible();
281 float deviceScaleFactor = document().deviceScaleFactor();
282 FloatRect pixelSnappedRect = snapRectToDevicePixels(rect, deviceScaleFactor);
283
284 auto borderShapeRespectingBleedAvoidance = [&](RectEdges<bool> closedEdges, bool shrinkForBleedAvoidance = true) {
285 auto borderRect = rect;
286 if (shrinkForBleedAvoidance && bleedAvoidance == BleedAvoidance::ShrinkBackground) {
287 // Ideally we'd use the border rect, but add a device pixel of additional inset to preserve corner shape.
288 borderRect = shrinkRectByOneDevicePixel(m_paintInfo.context(), borderRect, deviceScaleFactor);
289 }
290
291 return BorderShape::shapeForBorderRect(style, borderRect, closedEdges);
292 };
293
294 // Fast path for drawing simple color backgrounds.
295 if (!isRoot && !clippedWithLocalScrolling && !shouldPaintBackgroundImage && isBorderFill && layer.isLast) {
296 if (!colorVisible)
297 return;
298
299 bool applyBoxShadowToBackground = boxShadowShouldBeAppliedToBackground(m_renderer, rect.location(), bleedAvoidance, inlineBoxIterator);
300 GraphicsContextStateSaver shadowStateSaver(context, applyBoxShadowToBackground);
301 if (applyBoxShadowToBackground)
302 applyBoxShadowForBackground(context, style);
303
304 if (hasRoundedBorder && bleedAvoidance != BleedAvoidance::UseTransparencyLayer) {
305 auto borderShape = borderShapeRespectingBleedAvoidance(closedEdges);
306 auto previousOperator = context.compositeOperation();
307 bool saveRestoreCompositeOp = op != previousOperator;
308 if (saveRestoreCompositeOp)
309 context.setCompositeOperation(op);
310
311 if (bleedAvoidance == BleedAvoidance::BackgroundOverBorder)
312 borderShape.fillInnerShape(context, bgColor, deviceScaleFactor);
313 else
314 borderShape.fillOuterShape(context, bgColor, deviceScaleFactor);
315
316 if (saveRestoreCompositeOp)
317 context.setCompositeOperation(previousOperator);
318 } else
319 context.fillRect(pixelSnappedRect, bgColor, op);
320
321 return;
322 }
323
324 auto clipForBorder = [&]() -> std::optional<FillBox> {
325 if (!hasRoundedBorder)
326 return { };
327
328 // FillBox::BorderBox radius clipping is taken care of by BleedAvoidance::UseTransparencyLayer
329 if (layerClip == FillBox::BorderBox && bleedAvoidance == BleedAvoidance::UseTransparencyLayer)
330 return { };
331
332 if (bleedAvoidance == BleedAvoidance::BackgroundOverBorder)
333 return FillBox::PaddingBox;
334
335 return layerClip;
336 }();
337
338 GraphicsContextStateSaver clipToBorderStateSaver(context, clipForBorder.has_value());
339 if (clipForBorder) {
340 switch (*clipForBorder) {
341 case FillBox::BorderBox:
342 case FillBox::BorderArea:
343 case FillBox::Text:
344 case FillBox::NoClip: {
345 auto borderShape = borderShapeRespectingBleedAvoidance(closedEdges, isBorderFill);
346 borderShape.clipToOuterShape(context, deviceScaleFactor);
347 break;
348 }
349 case FillBox::PaddingBox: {
350 auto borderShape = borderShapeRespectingBleedAvoidance(closedEdges, isBorderFill);
351 borderShape.clipToInnerShape(context, deviceScaleFactor);
352 break;
353 }
354 case FillBox::ContentBox: {
355 auto borderShape = m_renderer.borderShapeForContentClipping(rect);
356 borderShape.clipToInnerShape(context, deviceScaleFactor);
357 break;
358 }
359 }
360 }
361
362 LayoutUnit bLeft = closedEdges.left() ? m_renderer.borderLeft() : 0_lu;
363 LayoutUnit bRight = closedEdges.right() ? m_renderer.borderRight() : 0_lu;
364 LayoutUnit pLeft = closedEdges.left() ? m_renderer.paddingLeft() : 0_lu;
365 LayoutUnit pRight = closedEdges.right() ? m_renderer.paddingRight() : 0_lu;
366
367 GraphicsContextStateSaver clipWithScrollingStateSaver(context, clippedWithLocalScrolling);
368 LayoutRect scrolledPaintRect = rect;
369 if (clippedWithLocalScrolling) {
370 // Clip to the overflow area.
371 auto& renderBox = downcast<RenderBox>(m_renderer);
372 context.clip(renderBox.overflowClipRect(rect.location()));
373
374 // Adjust the paint rect to reflect a scrolled content box with borders at the ends.
375 scrolledPaintRect.moveBy(-renderBox.scrollPosition());
376 scrolledPaintRect.setWidth(bLeft + renderBox.layer()->scrollWidth() + bRight);
377 scrolledPaintRect.setHeight(renderBox.borderTop() + renderBox.layer()->scrollHeight() + renderBox.borderBottom());
378 }
379
380 GraphicsContextStateSaver backgroundClipStateSaver(context, false);
381
382 auto layerBlendMode = layer.layer.blendMode();
383
384 auto backgroundClipOuterLayerScope = TransparencyLayerScope(context, 1, false);
385 auto backgroundClipInnerLayerScope = TransparencyLayerScope(context, 1, false);
386
387 auto setupMaskingBackgroundClip = [&](const LayoutRect& borderRect, const std::function<void(GraphicsContext& context, const LayoutRect&, const FloatRect&)>& paintFunction) {
388 auto transparencyLayerBounds = snapRectToDevicePixels(rect, deviceScaleFactor);
389 transparencyLayerBounds.intersect(snapRectToDevicePixels(m_paintInfo.rect, deviceScaleFactor));
390 transparencyLayerBounds.inflate(1);
391
392 // The last layer is blended over the background color below, so only change the blend mode here for non-last layers.
393 if (layerBlendMode != BlendMode::Normal && !layer.isLast) {
394 context.setCompositeOperation(context.compositeOperation(), layerBlendMode);
395 layerBlendMode = BlendMode::Normal;
396 }
397
398 backgroundClipStateSaver.save();
399 context.clip(transparencyLayerBounds);
400 backgroundClipOuterLayerScope.beginLayer(1);
401
402 if (context.renderingMode() == RenderingMode::PDFDocument) {
403 // CG doesn't support some compositing operations when printing, so clip using an imageBuffer instead.
404 static constexpr auto printBufferResolution = 3.0f; // Chosen to result in output that is not too blurry.
405 RefPtr maskImage = context.createImageBuffer(transparencyLayerBounds.size(), printBufferResolution);
406 if (!maskImage)
407 return;
408
409 auto& maskContext = maskImage->context();
410 maskContext.translate(-transparencyLayerBounds.location());
411
412 paintFunction(maskContext, borderRect, transparencyLayerBounds);
413 context.clipToImageBuffer(*maskImage, transparencyLayerBounds);
414 } else {
415 paintFunction(context, borderRect, transparencyLayerBounds);
416 context.setCompositeOperation(CompositeOperator::SourceIn);
417 }
418
419 backgroundClipInnerLayerScope.beginLayer(1);
420 context.setCompositeOperation(CompositeOperator::SourceOver);
421 };
422
423 switch (layerClip) {
424 case FillBox::BorderBox:
425 break;
426 case FillBox::PaddingBox:
427 case FillBox::ContentBox: {
428 // Clip to the padding or content boxes as necessary.
429 if (!clipForBorder) {
430 bool includePadding = layerClip == FillBox::ContentBox;
431 LayoutRect clipRect = LayoutRect(scrolledPaintRect.x() + bLeft + (includePadding ? pLeft : 0_lu),
432 scrolledPaintRect.y() + m_renderer.borderTop() + (includePadding ? m_renderer.paddingTop() : 0_lu),
433 scrolledPaintRect.width() - bLeft - bRight - (includePadding ? pLeft + pRight : 0_lu),
434 scrolledPaintRect.height() - m_renderer.borderTop() - m_renderer.borderBottom() - (includePadding ? m_renderer.paddingTop() + m_renderer.paddingBottom() : 0_lu));
435 backgroundClipStateSaver.save();
436 context.clip(clipRect);
437 }
438 break;
439 }
440 case FillBox::Text: {
441 // We have to draw our text into a mask that can then be used to clip background drawing.
442 // First figure out how big the mask has to be. It should be no bigger than what we need
443 // to actually render, so we should intersect the dirty rect with the border box of the background.
444 setupMaskingBackgroundClip(rect, [&](GraphicsContext& context, const LayoutRect&, const FloatRect& paintRect) {
445 m_renderer.paintMaskForTextFillBox(context, paintRect, inlineBoxIterator, scrolledPaintRect);
446 });
447 break;
448 }
449 case FillBox::BorderArea: {
450 auto borderAreaPath = BorderPainter::pathForBorderArea(rect, style, deviceScaleFactor, closedEdges);
451 if (borderAreaPath) {
452 backgroundClipStateSaver.save();
453 context.clipPath(borderAreaPath.value());
454 break;
455 }
456
457 setupMaskingBackgroundClip(rect, [&](GraphicsContext& context, const LayoutRect& borderRect, const FloatRect& paintRect) {
458 auto borderPaintInfo = PaintInfo { context, LayoutRect { paintRect }, PaintPhase::BlockBackground, PaintBehavior::ForceBlackBorder };
459 auto borderPainter = BorderPainter { m_renderer, borderPaintInfo };
460 borderPainter.paintBorder(borderRect, style);
461 });
462 break;
463 }
464 case FillBox::NoClip:
465 break;
466 }
467
468 auto isOpaqueRoot = false;
469 if (isRoot) {
470 bool shouldPaintBaseBackground = view().rootElementShouldPaintBaseBackground();
471 isOpaqueRoot = !layer.isLast || bgColor.isOpaque() || shouldPaintBaseBackground;
472 if (!shouldPaintBaseBackground)
473 baseBgColorUsage = BaseBackgroundColorSkip;
474
475 view().frameView().setContentIsOpaque(isOpaqueRoot);
476 }
477
478 // Paint the color first underneath all images, culled if background image occludes it.
479 // FIXME: In the bgLayer.hasFiniteBounds() case, we could improve the culling test
480 // by verifying whether the background image covers the entire layout rect.
481 if (layer.isLast) {
482 LayoutRect backgroundRect(scrolledPaintRect);
483 bool applyBoxShadowToBackground = boxShadowShouldBeAppliedToBackground(m_renderer, rect.location(), bleedAvoidance, inlineBoxIterator);
484 if (applyBoxShadowToBackground || !shouldPaintBackgroundImage || !layer.layer.hasOpaqueImage(m_renderer) || !layer.layer.hasRepeatXY() || layer.layer.size().isEmpty()) {
485 if (!applyBoxShadowToBackground)
486 backgroundRect.intersect(m_paintInfo.rect);
487
488 // If we have an alpha and we are painting the root element, blend with the base background color.
489 Color baseColor;
490 bool shouldClearBackground = false;
491 if ((baseBgColorUsage != BaseBackgroundColorSkip) && isOpaqueRoot) {
492 baseColor = view().frameView().baseBackgroundColor();
493 if (!baseColor.isVisible())
494 shouldClearBackground = true;
495 }
496
497 GraphicsContextStateSaver shadowStateSaver(context, applyBoxShadowToBackground);
498 if (applyBoxShadowToBackground)
499 applyBoxShadowForBackground(context, style);
500
501 FloatRect backgroundRectForPainting = snapRectToDevicePixels(backgroundRect, deviceScaleFactor);
502 if (baseColor.isVisible()) {
503 if (!baseBgColorOnly && bgColor.isVisible())
504 baseColor = blendSourceOver(baseColor, bgColor);
505 context.fillRect(backgroundRectForPainting, baseColor, CompositeOperator::Copy);
506 } else if (!baseBgColorOnly && bgColor.isVisible()) {
507 auto operation = context.compositeOperation();
508 if (shouldClearBackground) {
509 if (op == CompositeOperator::DestinationOut) // We're punching out the background.
510 operation = op;
511 else
512 operation = CompositeOperator::Copy;
513 }
514 context.fillRect(backgroundRectForPainting, bgColor, operation);
515 } else if (shouldClearBackground)
516 context.clearRect(backgroundRectForPainting);
517 }
518 }
519
520 // no progressive loading of the background image
521 if (!baseBgColorOnly && shouldPaintBackgroundImage) {
522 // Multiline inline boxes paint like the image was one long strip spanning lines. The backgroundImageStrip is this fictional rectangle.
523 auto imageRect = backgroundImageStrip.isEmpty() ? scrolledPaintRect : backgroundImageStrip;
524 auto paintOffset = backgroundImageStrip.isEmpty() ? rect.location() : backgroundImageStrip.location();
525 auto geometry = calculateFillLayerImageGeometry(m_renderer, m_paintInfo.paintContainer, layer.layer, layer.zoom, paintOffset, imageRect, m_overrideOrigin);
526
527 auto& clientForBackgroundImage = backgroundObject ? *backgroundObject : m_renderer;
528 bgImage->setContainerContextForRenderer(clientForBackgroundImage, geometry.tileSizeWithoutPixelSnapping, m_renderer.style().usedZoom());
529
530 geometry.clip(LayoutRect(pixelSnappedRect));
531 RefPtr<Image> image;
532 bool isFirstLine = inlineBoxIterator && inlineBoxIterator->lineBox()->isFirst();
533 if (!geometry.destinationRect.isEmpty() && (image = bgImage->image(backgroundObject ? backgroundObject : &m_renderer, geometry.tileSize, context, isFirstLine))) {
534 context.setDrawLuminanceMask(layer.layer.maskMode() == Style::MaskMode::Luminance);
535
536 ImagePaintingOptions options = {
537 op == CompositeOperator::SourceOver ? layer.layer.compositeForPainting(layer.isLast) : op,
538 layerBlendMode,
539 m_renderer.decodingModeForImageDraw(*image, m_paintInfo),
540 ImageOrientation::Orientation::FromImage,
541 m_renderer.chooseInterpolationQuality(context, *image, &layer.layer, geometry.tileSize),
542 document().settings().imageSubsamplingEnabled() ? AllowImageSubsampling::Yes : AllowImageSubsampling::No,
543 document().settings().showDebugBorders() ? ShowDebugBackground::Yes : ShowDebugBackground::No,
544 m_paintInfo.paintBehavior.contains(PaintBehavior::DrawsHDRContent) ? DrawsHDRContent::Yes : DrawsHDRContent::No,
545 style.dynamicRangeLimit().toPlatformDynamicRangeLimit()
546 };
547
548 auto drawResult = context.drawTiledImage(*image, geometry.destinationRect, toLayoutPoint(geometry.relativePhase()), geometry.tileSize, geometry.spaceSize, options);
549 if (drawResult == ImageDrawResult::DidRequestDecoding) {
550 ASSERT(bgImage->hasCachedImage())((void)0);
551 bgImage->cachedImage()->addClientWaitingForAsyncDecoding(m_renderer.cachedImageClient());
552 }
553
554 if (!context.paintingDisabled()) {
555 if (m_renderer.element())
556 m_renderer.element()->setHasEverPaintedImages(true);
557
558 if (auto* image = bgImage->cachedImage(); image && image->currentFrameIsComplete(&m_renderer)) {
Local variable 'image' is uncounted and unsafe
559 if (auto styleable = Styleable::fromRenderer(m_renderer))
560 document().didPaintImage(styleable->element, image, geometry.destinationRect);
561 }
562 }
563 }
564 }
565}
566
567void BackgroundPainter::clipRoundedInnerRect(GraphicsContext& context, const FloatRoundedRect& clipRect)
568{
569 if (!clipRect.isRenderable()) [[unlikely]] {
570 auto adjustedClipRect = clipRect;
571 adjustedClipRect.adjustRadii();
572 context.clipRoundedRect(adjustedClipRect);
573 return;
574 }
575
576 context.clipRoundedRect(clipRect);
577}
578
579static inline std::optional<LayoutUnit> NODELETE[[clang::annotate_type("webkit.nodelete")]] getSpace(LayoutUnit areaSize, LayoutUnit tileSize)
580{
581 if (int numberOfTiles = areaSize / tileSize; numberOfTiles > 1)
582 return (areaSize - numberOfTiles * tileSize) / (numberOfTiles - 1);
583 return std::nullopt;
584}
585
586static void pixelSnapBackgroundImageGeometryForPainting(LayoutRect& destinationRect, LayoutSize& tileSize, LayoutSize& phase, LayoutSize& space, float scaleFactor)
587{
588 tileSize = LayoutSize(snapRectToDevicePixels(LayoutRect(destinationRect.location(), tileSize), scaleFactor).size());
589 phase = LayoutSize(snapRectToDevicePixels(LayoutRect(destinationRect.location(), phase), scaleFactor).size());
590 space = LayoutSize(snapRectToDevicePixels(LayoutRect(LayoutPoint(), space), scaleFactor).size());
591 destinationRect = LayoutRect(snapRectToDevicePixels(destinationRect, scaleFactor));
592}
593
594BackgroundImageGeometry BackgroundPainter::calculateFillLayerImageGeometry(const RenderBoxModelObject& renderer, const RenderLayerModelObject* paintContainer, const Style::BackgroundLayer& fillLayer, Style::ZoomFactor zoom, const LayoutPoint& paintOffset, const LayoutRect& borderBoxRect, std::optional<FillBox> overrideOrigin)
595{
596 return calculateFillLayerImageGeometryImpl(renderer, paintContainer, fillLayer, zoom, paintOffset, borderBoxRect, overrideOrigin);
597}
598
599BackgroundImageGeometry BackgroundPainter::calculateFillLayerImageGeometry(const RenderBoxModelObject& renderer, const RenderLayerModelObject* paintContainer, const Style::MaskLayer& fillLayer, Style::ZoomFactor zoom, const LayoutPoint& paintOffset, const LayoutRect& borderBoxRect, std::optional<FillBox> overrideOrigin)
600{
601 return calculateFillLayerImageGeometryImpl(renderer, paintContainer, fillLayer, zoom, paintOffset, borderBoxRect, overrideOrigin);
602}
603
604template<typename Layer> BackgroundImageGeometry BackgroundPainter::calculateFillLayerImageGeometryImpl(const RenderBoxModelObject& renderer, const RenderLayerModelObject* paintContainer, const Layer& fillLayer, Style::ZoomFactor zoom, const LayoutPoint& paintOffset, const LayoutRect& borderBoxRect, std::optional<FillBox> overrideOrigin)
605{
606 auto& view = renderer.view();
607
608 LayoutUnit left;
609 LayoutUnit top;
610 LayoutSize positioningAreaSize;
611 // Determine the background positioning area and set destination rect to the background painting area.
612 // Destination rect will be adjusted later if the background is non-repeating.
613 CheckedPtr enclosingLayer = renderer.enclosingLayer();
614 bool isTransformed = renderer.isTransformed() || (enclosingLayer && enclosingLayer->hasTransformedAncestor());
615 bool fixedAttachment = fillLayer.attachment() == FillAttachment::FixedBackground && !isTransformed;
616
617 LayoutRect destinationRect(borderBoxRect);
618 float deviceScaleFactor = renderer.document().deviceScaleFactor();
619 if (!fixedAttachment) {
620 LayoutUnit right;
621 LayoutUnit bottom;
622 // Scroll and Local.
623 auto fillLayerOrigin = overrideOrigin.value_or(fillLayer.origin());
624 if (fillLayerOrigin != FillBox::BorderBox) {
625 left = renderer.borderLeft();
626 right = renderer.borderRight();
627 top = renderer.borderTop();
628 bottom = renderer.borderBottom();
629 if (fillLayerOrigin == FillBox::ContentBox) {
630 left += renderer.paddingLeft();
631 right += renderer.paddingRight();
632 top += renderer.paddingTop();
633 bottom += renderer.paddingBottom();
634 }
635 }
636
637 // The background of the box generated by the root element covers the entire canvas including
638 // its margins. Since those were added in already, we have to factor them out when computing
639 // the background positioning area.
640 if (renderer.isDocumentElementRenderer()) {
641 positioningAreaSize = downcast<RenderBox>(renderer).size() - LayoutSize(left + right, top + bottom);
642 positioningAreaSize = LayoutSize(snapSizeToDevicePixel(positioningAreaSize, LayoutPoint(), deviceScaleFactor));
643 if (view.frameView().hasExtendedBackgroundRectForPainting()) {
644 LayoutRect extendedBackgroundRect = view.frameView().extendedBackgroundRectForPainting();
645 left += (renderer.marginLeft() - extendedBackgroundRect.x());
646 top += (renderer.marginTop() - extendedBackgroundRect.y());
647 }
648 } else {
649 positioningAreaSize = borderBoxRect.size() - LayoutSize(left + right, top + bottom);
650 positioningAreaSize = LayoutSize(snapRectToDevicePixels(LayoutRect(paintOffset, positioningAreaSize), deviceScaleFactor).size());
651 }
652 } else {
653 LayoutRect viewportRect;
654 FloatBoxExtent obscuredContentInsets;
655 if (renderer.settings().fixedBackgroundsPaintRelativeToDocument())
656 viewportRect = view.unscaledDocumentRect();
657 else {
658 LocalFrameView& frameView = view.frameView();
659 bool useFixedLayout = frameView.useFixedLayout() && !frameView.fixedLayoutSize().isEmpty();
660
661 if (useFixedLayout) {
662 // Use the fixedLayoutSize() when useFixedLayout() because the rendering will scale
663 // down the frameView to to fit in the current viewport.
664 viewportRect.setSize(frameView.fixedLayoutSize());
665 } else
666 viewportRect.setSize(frameView.sizeForVisibleContent());
667
668 if (renderer.fixedBackgroundPaintsInLocalCoordinates()) {
669 if (!useFixedLayout) {
670 // Shifting location by the content insets is needed for layout tests which expect
671 // layout to be shifted when calling window.internals.setObscuredContentInsets().
672 obscuredContentInsets = frameView.obscuredContentInsets(ScrollView::InsetType::WebCoreOrPlatformInset);
673 viewportRect.setLocation({ -obscuredContentInsets.left(), -obscuredContentInsets.top() });
674 }
675 } else if (useFixedLayout || frameView.frameScaleFactor() != 1) {
676 // scrollPositionForFixedPosition() is adjusted for page scale and it does not include
677 // insets so do not add it to the calculation below.
678 viewportRect.setLocation(frameView.scrollPositionForFixedPosition());
679 } else {
680 // documentScrollPositionRelativeToViewOrigin() is already adjusted for content insets
681 // so we need to account for that in calculating the phase size
682 obscuredContentInsets = frameView.obscuredContentInsets(ScrollView::InsetType::WebCoreOrPlatformInset);
683 viewportRect.setLocation(frameView.documentScrollPositionRelativeToViewOrigin());
684 }
685
686 left += obscuredContentInsets.left();
687 top += obscuredContentInsets.top();
688 }
689
690 if (paintContainer)
691 viewportRect.moveBy(LayoutPoint(-paintContainer->localToAbsolute(FloatPoint())));
692
693 destinationRect = viewportRect;
694 positioningAreaSize = destinationRect.size();
695 positioningAreaSize.setWidth(positioningAreaSize.width() - obscuredContentInsets.left());
696 positioningAreaSize.setHeight(positioningAreaSize.height() - obscuredContentInsets.top());
697 positioningAreaSize = LayoutSize(snapRectToDevicePixels(LayoutRect(destinationRect.location(), positioningAreaSize), deviceScaleFactor).size());
698 }
699
700 LayoutSize tileSize = calculateFillTileSize(renderer, fillLayer, zoom, positioningAreaSize);
701
702 auto backgroundRepeatX = fillLayer.repeat().x();
703 auto backgroundRepeatY = fillLayer.repeat().y();
704 LayoutUnit availableWidth = positioningAreaSize.width() - tileSize.width();
705 LayoutUnit availableHeight = positioningAreaSize.height() - tileSize.height();
706
707 LayoutSize spaceSize;
708 LayoutSize phase;
709 auto computedXPosition = Style::evaluate<LayoutUnit>(fillLayer.positionX(), availableWidth, zoom);
710 if (backgroundRepeatX == FillRepeat::Round && positioningAreaSize.width() > 0 && tileSize.width() > 0) {
711 int numTiles = std::max(1, roundToInt(positioningAreaSize.width() / tileSize.width()));
712 if (!fillLayer.size().specifiedHeight() && backgroundRepeatY != FillRepeat::Round)
713 tileSize.setHeight(tileSize.height() * positioningAreaSize.width() / (numTiles * tileSize.width()));
714
715 tileSize.setWidth(positioningAreaSize.width() / numTiles);
716 phase.setWidth(tileSize.width() ? tileSize.width() - fmodf((computedXPosition + left), tileSize.width()) : 0);
717 }
718
719 auto computedYPosition = Style::evaluate<LayoutUnit>(fillLayer.positionY(), availableHeight, zoom);
720 if (backgroundRepeatY == FillRepeat::Round && positioningAreaSize.height() > 0 && tileSize.height() > 0) {
721 int numTiles = std::max(1, roundToInt(positioningAreaSize.height() / tileSize.height()));
722 if (!fillLayer.size().specifiedWidth() && backgroundRepeatX != FillRepeat::Round)
723 tileSize.setWidth(tileSize.width() * positioningAreaSize.height() / (numTiles * tileSize.height()));
724
725 tileSize.setHeight(positioningAreaSize.height() / numTiles);
726 phase.setHeight(tileSize.height() ? tileSize.height() - fmodf((computedYPosition + top), tileSize.height()) : 0);
727 }
728
729 if (backgroundRepeatX == FillRepeat::Repeat) {
730 phase.setWidth(tileSize.width() ? tileSize.width() - fmodf(computedXPosition + left, tileSize.width()) : 0);
731 spaceSize.setWidth(0);
732 } else if (backgroundRepeatX == FillRepeat::Space && tileSize.width() > 0) {
733 if (auto space = getSpace(positioningAreaSize.width(), tileSize.width())) {
734 LayoutUnit actualWidth = tileSize.width() + *space;
735 computedXPosition = 0;
736 spaceSize.setWidth(*space);
737 spaceSize.setHeight(0);
738 phase.setWidth(actualWidth ? actualWidth - fmodf((computedXPosition + left), actualWidth) : 0);
739 } else
740 backgroundRepeatX = FillRepeat::NoRepeat;
741 }
742
743 if (backgroundRepeatX == FillRepeat::NoRepeat) {
744 LayoutUnit xOffset = left + computedXPosition;
745 if (xOffset > 0)
746 destinationRect.move(xOffset, 0_lu);
747 xOffset = std::min<LayoutUnit>(xOffset, 0);
748 phase.setWidth(-xOffset);
749 destinationRect.setWidth(tileSize.width() + xOffset);
750 spaceSize.setWidth(0);
751 }
752
753 if (backgroundRepeatY == FillRepeat::Repeat) {
754 phase.setHeight(tileSize.height() ? tileSize.height() - fmodf(computedYPosition + top, tileSize.height()) : 0);
755 spaceSize.setHeight(0);
756 } else if (backgroundRepeatY == FillRepeat::Space && tileSize.height() > 0) {
757 if (auto space = getSpace(positioningAreaSize.height(), tileSize.height())) {
758 LayoutUnit actualHeight = tileSize.height() + *space;
759 computedYPosition = 0;
760 spaceSize.setHeight(*space);
761 phase.setHeight(actualHeight ? actualHeight - fmodf((computedYPosition + top), actualHeight) : 0);
762 } else
763 backgroundRepeatY = FillRepeat::NoRepeat;
764 }
765 if (backgroundRepeatY == FillRepeat::NoRepeat) {
766 LayoutUnit yOffset = top + computedYPosition;
767 if (yOffset > 0)
768 destinationRect.move(0_lu, yOffset);
769 yOffset = std::min<LayoutUnit>(yOffset, 0);
770 phase.setHeight(-yOffset);
771 destinationRect.setHeight(tileSize.height() + yOffset);
772 spaceSize.setHeight(0);
773 }
774
775 if (fixedAttachment) {
776 LayoutPoint attachmentPoint = borderBoxRect.location();
777 phase.expand(std::max<LayoutUnit>(attachmentPoint.x() - destinationRect.x(), 0), std::max<LayoutUnit>(attachmentPoint.y() - destinationRect.y(), 0));
778 }
779
780 destinationRect.intersect(borderBoxRect);
781
782 auto tileSizeWithoutPixelSnapping = tileSize;
783 pixelSnapBackgroundImageGeometryForPainting(destinationRect, tileSize, phase, spaceSize, deviceScaleFactor);
784
785 return BackgroundImageGeometry(destinationRect, tileSizeWithoutPixelSnapping, tileSize, phase, spaceSize, fixedAttachment);
786}
787
788template<typename Layer> LayoutSize BackgroundPainter::calculateFillTileSize(const RenderBoxModelObject& renderer, const Layer& fillLayer, Style::ZoomFactor, const LayoutSize& positioningAreaSize)
789{
790 RefPtr image = fillLayer.image().tryStyleImage();
791 auto devicePixelSize = LayoutUnit { 1.0 / renderer.document().deviceScaleFactor() };
792
793 LayoutSize imageIntrinsicSize;
794 if (image) {
795 imageIntrinsicSize = renderer.calculateImageIntrinsicDimensions(image.get(), positioningAreaSize, RenderBoxModelObject::ScaleByUsedZoom::Yes);
796 imageIntrinsicSize.scale(1 / image->imageScaleFactor(), 1 / image->imageScaleFactor());
797 } else
798 imageIntrinsicSize = positioningAreaSize;
799
800 auto handleKeyword = [&](auto keyword) -> LayoutSize {
801 // Scale computation needs higher precision than what LayoutUnit can offer.
802 FloatSize localImageIntrinsicSize = imageIntrinsicSize;
803 FloatSize localPositioningAreaSize = positioningAreaSize;
804
805 float horizontalScaleFactor = localImageIntrinsicSize.width() ? (localPositioningAreaSize.width() / localImageIntrinsicSize.width()) : 1;
806 float verticalScaleFactor = localImageIntrinsicSize.height() ? (localPositioningAreaSize.height() / localImageIntrinsicSize.height()) : 1;
807 float scaleFactor = keyword.value == CSSValueContain ? std::min(horizontalScaleFactor, verticalScaleFactor) : std::max(horizontalScaleFactor, verticalScaleFactor);
808
809 if (localImageIntrinsicSize.isEmpty())
810 return { };
811
812 return LayoutSize(localImageIntrinsicSize.scaled(scaleFactor).expandedTo({ devicePixelSize, devicePixelSize }));
813 };
814
815 return WTF::switchOn(fillLayer.size(),
816 [&](const CSS::Keyword::Cover& keyword) {
817 return handleKeyword(keyword);
818 },
819 [&](const CSS::Keyword::Contain& keyword) {
820 return handleKeyword(keyword);
821 },
822 [&](const Style::BackgroundSize::LengthSize& size) {
823 auto tileSize = positioningAreaSize;
824
825 auto& layerWidth = size.width();
826 auto& layerHeight = size.height();
827
828 if (auto fixed = layerWidth.tryFixed())
829 tileSize.setWidth(Style::evaluate<LayoutUnit>(*fixed, Style::ZoomNeeded { }));
830 else if (layerWidth.isPercentOrCalculated()) {
831 auto resolvedWidth = Style::evaluate<LayoutUnit>(layerWidth, positioningAreaSize.width(), Style::ZoomNeeded { });
832 // Non-zero resolved value should always produce some content.
833 tileSize.setWidth(!resolvedWidth ? resolvedWidth : std::max(devicePixelSize, resolvedWidth));
834 }
835
836 if (auto fixed = layerHeight.tryFixed())
837 tileSize.setHeight(Style::evaluate<LayoutUnit>(*fixed, Style::ZoomNeeded { }));
838 else if (layerHeight.isPercentOrCalculated()) {
839 auto resolvedHeight = Style::evaluate<LayoutUnit>(layerHeight, positioningAreaSize.height(), Style::ZoomNeeded { });
840 // Non-zero resolved value should always produce some content.
841 tileSize.setHeight(!resolvedHeight ? resolvedHeight : std::max(devicePixelSize, resolvedHeight));
842 }
843
844 // If one of the values is auto we have to use the appropriate
845 // scale to maintain our aspect ratio.
846 bool hasNaturalAspectRatio = image && image->imageHasNaturalDimensions();
847 if (layerWidth.isAuto() && !layerHeight.isAuto()) {
848 if (hasNaturalAspectRatio && imageIntrinsicSize.height())
849 tileSize.setWidth(imageIntrinsicSize.width() * tileSize.height() / imageIntrinsicSize.height());
850 } else if (!layerWidth.isAuto() && layerHeight.isAuto()) {
851 if (hasNaturalAspectRatio && imageIntrinsicSize.width())
852 tileSize.setHeight(imageIntrinsicSize.height() * tileSize.width() / imageIntrinsicSize.width());
853 } else if (layerWidth.isAuto() && layerHeight.isAuto()) {
854 // If both width and height are auto, use the image's intrinsic size.
855 tileSize = imageIntrinsicSize;
856 }
857
858 tileSize.clampNegativeToZero();
859 return tileSize;
860 }
861 );
862}
863
864void BackgroundPainter::paintBoxShadow(const LayoutRect& paintRect, const RenderStyle& style, Style::ShadowStyle shadowStyle, RectEdges<bool> closedEdges) const
865{
866 // FIXME: Deal with border-image. Would be great to use border-image as a mask.
867 GraphicsContext& context = m_paintInfo.context();
868 if (context.paintingDisabled() || style.boxShadow().isNone())
869 return;
870
871 const auto borderShape = BorderShape::shapeForBorderRect(style, paintRect, closedEdges);
872
873 bool hasBorderRadius = style.border().hasBorderRadius();
874 float deviceScaleFactor = document().deviceScaleFactor();
875
876 bool hasOpaqueBackground = style.visitedDependentBackgroundColorApplyingColorFilter().isOpaque();
877 const auto& zoomFactor = style.usedZoomForLength();
878 for (const auto& shadow : style.boxShadow()) {
879 if (Style::shadowStyle(shadow) != shadowStyle)
880 continue;
881
882 LayoutSize shadowOffset(shadow.location.x().resolveZoom(zoomFactor), shadow.location.y().resolveZoom(zoomFactor));
883 LayoutUnit shadowPaintingExtent = Style::paintingExtent(shadow, zoomFactor);
884 LayoutUnit shadowSpread = LayoutUnit(shadow.spread.resolveZoom(zoomFactor));
885 auto shadowRadius = shadow.blur.resolveZoom(zoomFactor);
886
887 if (shadowOffset.isZero() && !shadowRadius && !shadowSpread)
888 continue;
889
890 Style::ColorResolver colorResolver { style };
891 auto shadowColor = colorResolver.colorResolvingCurrentColorApplyingColorFilter(shadow.color);
892
893 auto shouldInflateBorderRect = [&]() {
894 if (!hasOpaqueBackground)
895 return false;
896
897 // FIXME: The function to decide on the policy based on the transform should be a named function.
898 // FIXME: It's not clear if this check is right. What about integral scale factors?
899 auto transform = context.getCTM();
900 if (transform.a() != 1 || (transform.d() != 1 && transform.d() != -1) || transform.b() || transform.c())
901 return true;
902
903 return false;
904 };
905
906 if (!Style::isInset(shadow)) {
907 auto shadowShape = [&] {
908 if (!shadowSpread)
909 return borderShape;
910
911 if (shadowSpread > 0) {
912 auto spreadRect = paintRect;
913 spreadRect.inflate(shadowSpread);
914 return BorderShape::shapeForOutsetRect(style, paintRect, spreadRect, { }, closedEdges);
915 }
916
917 auto spreadRect = paintRect;
918 auto inflateX = std::max(shadowSpread, -paintRect.width() / 2);
919 auto inflateY = std::max(shadowSpread, -paintRect.height() / 2);
920 spreadRect.inflate(LayoutSize { inflateX, inflateY });
921 return BorderShape::shapeForInsetRect(style, paintRect, spreadRect /* , closedEdges*/);
922 }();
923
924 if (shadowShape.isEmpty())
925 continue;
926
927 // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time
928 // when painting the shadow. On the other hand, it introduces subpixel gaps along the
929 // corners. Those are avoided by insetting the clipping path by one pixel.
930 auto adjustedBorderShape = borderShape;
931 if (shouldInflateBorderRect())
932 adjustedBorderShape.inflate(-1_lu);
933
934 auto shadowRect = paintRect;
935 shadowRect.inflate(shadowPaintingExtent + shadowSpread);
936 shadowRect.move(shadowOffset);
937
938 if (!closedEdges.left())
939 shadowRect.shiftXEdgeTo(paintRect.x());
940
941 if (!closedEdges.top())
942 shadowRect.shiftYEdgeTo(paintRect.y());
943
944 if (!closedEdges.right())
945 shadowRect.shiftMaxXEdgeTo(paintRect.maxX());
946
947 if (!closedEdges.bottom())
948 shadowRect.shiftMaxYEdgeTo(paintRect.maxY());
949
950 auto pixelSnappedShadowRect = snapRectToDevicePixels(shadowRect, deviceScaleFactor);
951
952 GraphicsContextStateSaver stateSaver(context);
953 context.clip(pixelSnappedShadowRect);
954
955 // Move the fill just outside the clip, adding at least 1 pixel of separation so that the fill does not
956 // bleed in (due to antialiasing) if the context is transformed.
957 LayoutUnit xOffset = paintRect.width() + std::max<LayoutUnit>(0, shadowOffset.width()) + shadowPaintingExtent + 2 * shadowSpread + LayoutUnit(1);
958 LayoutSize extraOffset(xOffset.ceil(), 0);
959 shadowOffset -= extraOffset;
960 shadowShape.move(extraOffset);
961
962 auto pixelSnappedFillRect = shadowShape.snappedOuterRect(deviceScaleFactor);
963
964 LayoutPoint shadowRectOrigin = shadowShape.borderRect().location() + shadowOffset;
965 FloatPoint snappedShadowOrigin = FloatPoint(roundToDevicePixel(shadowRectOrigin.x(), deviceScaleFactor), roundToDevicePixel(shadowRectOrigin.y(), deviceScaleFactor));
966 FloatSize snappedShadowOffset = snappedShadowOrigin - pixelSnappedFillRect.location();
967
968 context.setDropShadow({ snappedShadowOffset, shadowRadius, shadowColor, shadow.isWebkitBoxShadow ? ShadowRadiusMode::Legacy : ShadowRadiusMode::Default });
969
970 adjustedBorderShape.clipOutOuterShape(context, deviceScaleFactor);
971
972 if (hasBorderRadius) {
973 auto influenceShape = BorderShape::shapeForBorderRect(style, shadowRect);
974 auto influenceRadii = influenceShape.radii();
975 influenceRadii.expand(2 * shadowPaintingExtent + shadowSpread);
976 influenceShape.setRadii(influenceRadii);
977
978 if (influenceShape.outerShapeContains(m_paintInfo.rect))
979 context.fillRect(shadowShape.snappedOuterRect(deviceScaleFactor), Color::black);
980 else
981 shadowShape.fillOuterShape(context, Color::black, deviceScaleFactor);
982 } else
983 context.fillRect(pixelSnappedFillRect, Color::black);
984 } else {
985 // Inset shadow.
986 auto borderWidthsWithSpread = borderShape.borderWidths() + RectEdges<LayoutUnit> { shadowSpread };
987
988 auto outerRectExpandedToObscureOpenEdges = paintRect;
989
990 auto shadowInfluence = shadowPaintingExtent + shadowSpread;
991 if (!closedEdges.left())
992 outerRectExpandedToObscureOpenEdges.shiftXEdgeBy(-(std::max<LayoutUnit>(shadowOffset.width(), 0) + shadowInfluence));
993 if (!closedEdges.top())
994 outerRectExpandedToObscureOpenEdges.shiftYEdgeBy(-(std::max<LayoutUnit>(shadowOffset.height(), 0) + shadowInfluence));
995 if (!closedEdges.right())
996 outerRectExpandedToObscureOpenEdges.setWidth(outerRectExpandedToObscureOpenEdges.width() - std::min<LayoutUnit>(shadowOffset.width(), 0) + shadowInfluence);
997 if (!closedEdges.bottom())
998 outerRectExpandedToObscureOpenEdges.setHeight(outerRectExpandedToObscureOpenEdges.height() - std::min<LayoutUnit>(shadowOffset.height(), 0) + shadowInfluence);
999
1000 auto shapeForInnerHole = BorderShape(outerRectExpandedToObscureOpenEdges, borderWidthsWithSpread, borderShape.radii());
1001 if (shapeForInnerHole.snappedInnerRect(deviceScaleFactor).isEmpty()) {
1002 shapeForInnerHole.fillOuterShape(context, shadowColor, deviceScaleFactor);
1003 continue;
1004 }
1005
1006 auto areaCastingShadowInHole = [](const LayoutRect& holeRect, LayoutUnit shadowExtent, LayoutUnit shadowSpread, const LayoutSize& shadowOffset) {
1007 auto bounds = holeRect;
1008 bounds.inflate(shadowExtent);
1009
1010 if (shadowSpread < 0)
1011 bounds.inflate(-shadowSpread);
1012
1013 LayoutRect offsetBounds = bounds;
1014 offsetBounds.move(-shadowOffset);
1015 return unionRect(bounds, offsetBounds);
1016 };
1017
1018 auto fillColor = shadowColor.opaqueColor();
1019 auto shadowCastingRect = areaCastingShadowInHole(borderShape.innerEdgeRect(), shadowPaintingExtent, shadowSpread, shadowOffset);
1020
1021 GraphicsContextStateSaver stateSaver(context);
1022
1023 borderShape.clipToInnerShape(context, deviceScaleFactor);
1024
1025 LayoutUnit xOffset = 2 * paintRect.width() + std::max<LayoutUnit>(0, shadowOffset.width()) + shadowPaintingExtent - 2 * shadowSpread + LayoutUnit(1);
1026 LayoutSize extraOffset(xOffset.ceil(), 0);
1027
1028 context.translate(extraOffset);
1029 shadowOffset -= extraOffset;
1030
1031 auto snappedShadowOffset = roundSizeToDevicePixels(shadowOffset, deviceScaleFactor);
1032 context.setDropShadow({ snappedShadowOffset, shadowRadius, shadowColor, shadow.isWebkitBoxShadow ? ShadowRadiusMode::Legacy : ShadowRadiusMode::Default });
1033
1034 shapeForInnerHole.fillRectWithInnerHoleShape(context, shadowCastingRect, fillColor, deviceScaleFactor);
1035 }
1036 }
1037}
1038
1039bool BackgroundPainter::boxShadowShouldBeAppliedToBackground(const RenderBoxModelObject& renderer, const LayoutPoint& paintOffset, BleedAvoidance bleedAvoidance, const InlineIterator::InlineBoxIterator& inlineBox)
1040{
1041 if (bleedAvoidance != BleedAvoidance::None)
1042 return false;
1043
1044 auto& style = renderer.style();
1045
1046 if (style.hasUsedAppearance())
1047 return false;
1048
1049 bool hasOneNormalBoxShadow = false;
1050 for (const auto& currentShadow : style.boxShadow()) {
1051 if (Style::isInset(currentShadow))
1052 continue;
1053
1054 if (hasOneNormalBoxShadow)
1055 return false;
1056 hasOneNormalBoxShadow = true;
1057
1058 if (!Style::isZero(currentShadow.spread))
1059 return false;
1060 }
1061
1062 if (!hasOneNormalBoxShadow)
1063 return false;
1064
1065 Color backgroundColor = style.visitedDependentBackgroundColorApplyingColorFilter();
1066 if (!backgroundColor.isOpaque())
1067 return false;
1068
1069 auto& lastBackgroundLayer = style.backgroundLayers().usedLast();
1070
1071 if (lastBackgroundLayer.clip() != FillBox::BorderBox)
1072 return false;
1073
1074 RefPtr image = lastBackgroundLayer.image().tryStyleImage();
1075 if (image && style.border().hasBorderRadius())
1076 return false;
1077
1078 auto applyToInlineBox = [&] {
1079 // The checks here match how paintFillLayer() decides whether to clip (if it does, the shadow
1080 // would be clipped out, so it has to be drawn separately).
1081 if (inlineBox->isRootInlineBox())
1082 return true;
1083 if (!inlineBox->nextInlineBoxLineLeftward() && !inlineBox->nextInlineBoxLineRightward())
1084 return true;
1085 auto& renderer = inlineBox->renderer();
1086 bool hasFillImage = image && image->canRender(&renderer, renderer.style().usedZoom());
1087 return !hasFillImage && !renderer.style().border().hasBorderRadius();
1088 };
1089
1090 if (inlineBox && !applyToInlineBox())
1091 return false;
1092
1093 if (renderer.hasNonVisibleOverflow() && lastBackgroundLayer.attachment() == FillAttachment::LocalBackground)
1094 return false;
1095
1096 if (is<RenderTableCell>(renderer))
1097 return false;
1098
1099 if (auto* imageRenderer = dynamicDowncast<RenderImage>(renderer))
1100 return !const_cast<RenderImage&>(*imageRenderer).backgroundIsKnownToBeObscured(paintOffset);
1101
1102 return true;
1103}
1104
1105const Document& BackgroundPainter::document() const
1106{
1107 return m_renderer.document();
1108}
1109
1110const RenderView& BackgroundPainter::view() const
1111{
1112 return m_renderer.view();
1113}
1114
1115}