Bug Summary

File:Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/inspector/InspectorCanvas.cpp
Warning:line 548, column 30
Local variable 'cachedImage' 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 UnifiedSource258.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/UnifiedSource258.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/UnifiedSource258.plist -x c++ /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebCore/unified-sources/UnifiedSource258.cpp
1/*
2 * Copyright (C) 2017-2023 Apple Inc. All rights reserved.
3 * Copyright (C) 2025 Samuel Weinig <sam@webkit.org>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "InspectorCanvas.h"
29
30#include "AffineTransform.h"
31#include "CSSStyleImageValue.h"
32#include "CachedImage.h"
33#include "CanvasGradient.h"
34#include "CanvasPattern.h"
35#include "CanvasRenderingContext2D.h"
36#include "ColorSerialization.h"
37#include "DOMMatrix2DInit.h"
38#include "DOMPointInit.h"
39#include "Document.h"
40#include "Element.h"
41#include "FloatPoint.h"
42#include "Gradient.h"
43#include "HTMLCanvasElement.h"
44#include "HTMLImageElement.h"
45#include "HTMLVideoElement.h"
46#include "Image.h"
47#include "ImageBitmap.h"
48#include "ImageBitmapRenderingContext.h"
49#include "ImageBuffer.h"
50#include "ImageData.h"
51#include "ImageUtilities.h"
52#include "InspectorCanvasAgent.h"
53#include "InspectorDOMAgent.h"
54#include "InspectorInstrumentation.h"
55#include "JSCanvasDirection.h"
56#include "JSCanvasFillRule.h"
57#include "JSCanvasLineCap.h"
58#include "JSCanvasLineJoin.h"
59#include "JSCanvasRenderingContext2D.h"
60#include "JSCanvasTextAlign.h"
61#include "JSCanvasTextBaseline.h"
62#include "JSExecState.h"
63#include "JSImageBitmapRenderingContext.h"
64#include "JSImageSmoothingQuality.h"
65#include "JSPredefinedColorSpace.h"
66#include "JSWebGL2RenderingContext.h"
67#include "JSWebGLRenderingContext.h"
68#include "Path2D.h"
69#include "SVGPathUtilities.h"
70#include "StringAdaptors.h"
71#include <JavaScriptCore/IdentifiersFactory.h>
72#include <JavaScriptCore/JSCInlines.h>
73#include <JavaScriptCore/ScriptCallStackFactory.h>
74#include <wtf/Function.h>
75#include <wtf/RefPtr.h>
76#include <wtf/Scope.h>
77#include <wtf/Vector.h>
78#include <wtf/text/MakeString.h>
79#include <wtf/text/WTFString.h>
80
81#if ENABLE(OFFSCREEN_CANVAS)(defined 1 && 1)
82#include "JSOffscreenCanvasRenderingContext2D.h"
83#include "OffscreenCanvas.h"
84#include "OffscreenCanvasRenderingContext2D.h"
85#endif
86
87namespace WebCore {
88
89using namespace Inspector;
90
91Ref<InspectorCanvas> InspectorCanvas::create(CanvasRenderingContext& context)
92{
93 return adoptRef(*new InspectorCanvas(context));
94}
95
96InspectorCanvas::InspectorCanvas(CanvasRenderingContext& context)
97 : m_identifier(makeString("canvas:"_s, IdentifiersFactory::createIdentifier()))
98 , m_context(context)
99{
100}
101
102HTMLCanvasElement* InspectorCanvas::canvasElement() const
103{
104 return dynamicDowncast<HTMLCanvasElement>(m_context->canvasBase());
105}
106
107ScriptExecutionContext* InspectorCanvas::scriptExecutionContext() const
108{
109 return m_context->canvasBase().scriptExecutionContext();
110}
111
112JSC::JSValue InspectorCanvas::resolveContext(JSC::JSGlobalObject* exec)
113{
114 JSC::JSLockHolder lock(exec);
115 auto* globalObject = deprecatedGlobalObjectForPrototype(exec);
116 if (is<CanvasRenderingContext2D>(m_context))
117 return toJS(exec, globalObject, downcast<CanvasRenderingContext2D>(m_context.get()));
118#if ENABLE(OFFSCREEN_CANVAS)(defined 1 && 1)
119 if (is<OffscreenCanvasRenderingContext2D>(m_context))
120 return toJS(exec, globalObject, downcast<OffscreenCanvasRenderingContext2D>(m_context.get()));
121#endif
122 if (is<ImageBitmapRenderingContext>(m_context))
123 return toJS(exec, globalObject, downcast<ImageBitmapRenderingContext>(m_context.get()));
124#if ENABLE(WEBGL)(defined 1 && 1)
125 if (is<WebGLRenderingContext>(m_context))
126 return toJS(exec, globalObject, downcast<WebGLRenderingContext>(m_context.get()));
127 if (is<WebGL2RenderingContext>(m_context))
128 return toJS(exec, globalObject, downcast<WebGL2RenderingContext>(m_context.get()));
129#endif
130 RELEASE_ASSERT_NOT_REACHED()do { WTF::isIntegralOrPointerType(); compilerFenceForCrash();
WTFCrashWithInfo(130, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/inspector/InspectorCanvas.cpp"
, __PRETTY_FUNCTION__ ); } while (false)
;
131}
132
133HashSet<Element*> InspectorCanvas::clientNodes() const
134{
135 return m_context->canvasBase().cssCanvasClients();
136}
137
138void InspectorCanvas::canvasChanged()
139{
140 if (!m_context->hasActiveInspectorCanvasCallTracer())
141 return;
142
143 // Since 2D contexts are able to be fully reproduced in the frontend, we don't need snapshots.
144 if (is<CanvasRenderingContext2D>(m_context))
145 return;
146#if ENABLE(OFFSCREEN_CANVAS)(defined 1 && 1)
147 if (is<OffscreenCanvasRenderingContext2D>(m_context))
148 return;
149#endif
150
151 m_contentChanged = true;
152}
153
154void InspectorCanvas::resetRecordingData()
155{
156 m_initialState = nullptr;
157 m_frames = nullptr;
158 m_currentActions = nullptr;
159 m_serializedDuplicateData = nullptr;
160 m_indexedDuplicateData.clear();
161 m_recordingName = { };
162 m_bufferLimit = 100 * 1024 * 1024;
163 m_bufferUsed = 0;
164 m_frameCount = std::nullopt;
165 m_framesCaptured = 0;
166 m_contentChanged = false;
167
168 // FIXME: <https://webkit.org/b/201651> Web Inspector: Canvas: support canvas recordings for WebGPUDevice
169
170 m_context->setHasActiveInspectorCanvasCallTracer(false);
171}
172
173bool InspectorCanvas::hasRecordingData() const
174{
175 return m_bufferUsed > 0;
176}
177
178bool InspectorCanvas::currentFrameHasData() const
179{
180 return !!m_frames;
181}
182
183template<typename T> static Ref<JSON::ArrayOf<JSON::Value>> buildArrayForVector(const Vector<T>& vector)
184{
185 auto array = JSON::ArrayOf<JSON::Value>::create();
186 for (auto& item : vector)
187 array->addItem(item);
188 return array;
189}
190
191static bool shouldSnapshotBitmapRendererAction(const String& name)
192{
193 return name == "transferFromImageBitmap"_s;
194}
195
196#if ENABLE(WEBGL)(defined 1 && 1)
197static bool shouldSnapshotWebGLAction(const String& name)
198{
199 return name == "clear"_s
200 || name == "drawArrays"_s
201 || name == "drawElements"_s;
202}
203
204static bool shouldSnapshotWebGL2Action(const String& name)
205{
206 return name == "clear"_s
207 || name == "drawArrays"_s
208 || name == "drawArraysInstanced"_s
209 || name == "drawElements"_s
210 || name == "drawElementsInstanced"_s;
211}
212#endif
213
214void InspectorCanvas::recordAction(String&& name, InspectorCanvasProcessedArguments&& arguments)
215{
216 if (!m_initialState) {
217 // We should only construct the initial state for the first action of the recording.
218 ASSERT(!m_frames && !m_currentActions)((void)0);
219
220 m_initialState = buildInitialState();
221 m_bufferUsed += m_initialState->memoryCost();
222 }
223
224 if (!m_frames)
225 m_frames = JSON::ArrayOf<Inspector::Protocol::Recording::Frame>::create();
226
227 if (!m_currentActions) {
228 m_currentActions = JSON::ArrayOf<JSON::Value>::create();
229
230 auto frame = Inspector::Protocol::Recording::Frame::create()
231 .setActions(*m_currentActions)
232 .release();
233
234 m_frames->addItem(WTF::move(frame));
235 ++m_framesCaptured;
236
237 m_currentFrameStartTime = MonotonicTime::now();
238 }
239
240 appendActionSnapshotIfNeeded();
241
242 // FIXME: <https://webkit.org/b/201651> Web Inspector: Canvas: support canvas recordings for WebGPUDevice
243
244 if (is<ImageBitmapRenderingContext>(m_context) && shouldSnapshotBitmapRendererAction(name))
245 m_contentChanged = true;
246#if ENABLE(WEBGL)(defined 1 && 1)
247 else if (is<WebGLRenderingContext>(m_context) && shouldSnapshotWebGLAction(name))
248 m_contentChanged = true;
249 else if (is<WebGL2RenderingContext>(m_context) && shouldSnapshotWebGL2Action(name))
250 m_contentChanged = true;
251#endif
252
253 m_lastRecordedAction = buildAction(WTF::move(name), WTF::move(arguments));
254 m_bufferUsed += m_lastRecordedAction->memoryCost();
255 m_currentActions->addItem(*m_lastRecordedAction);
256}
257
258void InspectorCanvas::finalizeFrame()
259{
260 appendActionSnapshotIfNeeded();
261
262 if (m_frames && m_frames->length() && !m_currentFrameStartTime.isNaN()) {
263 auto currentFrame = unsafeRefDowncast<Inspector::Protocol::Recording::Frame>(m_frames->get(m_frames->length() - 1));
264 currentFrame->setDuration((MonotonicTime::now() - m_currentFrameStartTime).milliseconds());
265
266 m_currentFrameStartTime = MonotonicTime::nan();
267 }
268
269 m_currentActions = nullptr;
270}
271
272void InspectorCanvas::markCurrentFrameIncomplete()
273{
274 if (!m_currentActions || !m_frames || !m_frames->length())
275 return;
276
277 auto currentFrame = unsafeRefDowncast<Inspector::Protocol::Recording::Frame>(m_frames->get(m_frames->length() - 1));
278 currentFrame->setIncomplete(true);
279}
280
281void InspectorCanvas::setBufferLimit(long memoryLimit)
282{
283 m_bufferLimit = std::min<long>(memoryLimit, std::numeric_limits<int>::max());
284}
285
286bool InspectorCanvas::hasBufferSpace() const
287{
288 return m_bufferUsed < m_bufferLimit;
289}
290
291void InspectorCanvas::setFrameCount(long frameCount)
292{
293 if (frameCount > 0)
294 m_frameCount = std::min<long>(frameCount, std::numeric_limits<int>::max());
295 else
296 m_frameCount = std::nullopt;
297}
298
299bool InspectorCanvas::overFrameCount() const
300{
301 return m_frameCount && m_framesCaptured >= m_frameCount.value();
302}
303
304static RefPtr<Inspector::Protocol::Canvas::ContextAttributes> buildObjectForCanvasContextAttributes(CanvasRenderingContext& context)
305{
306 if (is<CanvasRenderingContext2DBase>(context)) {
307 auto attributes = downcast<CanvasRenderingContext2DBase>(context).getContextAttributes();
308 auto contextAttributesPayload = Inspector::Protocol::Canvas::ContextAttributes::create()
309 .release();
310 switch (attributes.colorSpace) {
311 case PredefinedColorSpace::SRGB:
312 contextAttributesPayload->setColorSpace(Inspector::Protocol::Canvas::ColorSpace::SRGB);
313 break;
314 case PredefinedColorSpace::SRGBLinear:
315 contextAttributesPayload->setColorSpace(Inspector::Protocol::Canvas::ColorSpace::SRGBLinear);
316 break;
317#if ENABLE(PREDEFINED_COLOR_SPACE_DISPLAY_P3)(defined 1 && 1)
318 case PredefinedColorSpace::DisplayP3:
319 contextAttributesPayload->setColorSpace(Inspector::Protocol::Canvas::ColorSpace::DisplayP3);
320 break;
321 case PredefinedColorSpace::DisplayP3Linear:
322 contextAttributesPayload->setColorSpace(Inspector::Protocol::Canvas::ColorSpace::DisplayP3Linear);
323 break;
324#endif
325 }
326 contextAttributesPayload->setDesynchronized(attributes.desynchronized);
327 contextAttributesPayload->setWillReadFrequently(attributes.willReadFrequently);
328 return contextAttributesPayload;
329 }
330
331 if (is<ImageBitmapRenderingContext>(context)) {
332 auto contextAttributesPayload = Inspector::Protocol::Canvas::ContextAttributes::create()
333 .release();
334 contextAttributesPayload->setAlpha(downcast<ImageBitmapRenderingContext>(context).hasAlpha());
335 return contextAttributesPayload;
336 }
337
338#if ENABLE(WEBGL)(defined 1 && 1)
339 if (is<WebGLRenderingContextBase>(context)) {
340 const auto& attributes = downcast<WebGLRenderingContextBase>(context).getContextAttributes();
341 if (!attributes)
342 return nullptr;
343
344 auto contextAttributesPayload = Inspector::Protocol::Canvas::ContextAttributes::create()
345 .release();
346 contextAttributesPayload->setAlpha(attributes->alpha);
347 contextAttributesPayload->setDepth(attributes->depth);
348 contextAttributesPayload->setStencil(attributes->stencil);
349 contextAttributesPayload->setAntialias(attributes->antialias);
350 contextAttributesPayload->setPremultipliedAlpha(attributes->premultipliedAlpha);
351 contextAttributesPayload->setPreserveDrawingBuffer(attributes->preserveDrawingBuffer);
352 switch (attributes->powerPreference) {
353 case WebGLPowerPreference::Default:
354 contextAttributesPayload->setPowerPreference("default"_s);
355 break;
356 case WebGLPowerPreference::LowPower:
357 contextAttributesPayload->setPowerPreference("low-power"_s);
358 break;
359 case WebGLPowerPreference::HighPerformance:
360 contextAttributesPayload->setPowerPreference("high-performance"_s);
361 break;
362 }
363 contextAttributesPayload->setFailIfMajorPerformanceCaveat(attributes->failIfMajorPerformanceCaveat);
364 return contextAttributesPayload;
365 }
366#endif // ENABLE(WEBGL)
367
368 return nullptr;
369}
370
371Ref<Inspector::Protocol::Canvas::Canvas> InspectorCanvas::buildObjectForCanvas(bool captureBacktrace)
372{
373 auto contextType = [&] {
374 bool isOffscreen = false;
375#if ENABLE(OFFSCREEN_CANVAS)(defined 1 && 1)
376 if (is<OffscreenCanvas>(m_context->canvasBase()))
377 isOffscreen = true;
378#endif
379
380 if (is<CanvasRenderingContext2D>(m_context)) {
381 ASSERT(!isOffscreen)((void)0);
382 return Inspector::Protocol::Canvas::ContextType::Canvas2D;
383 }
384#if ENABLE(OFFSCREEN_CANVAS)(defined 1 && 1)
385 if (is<OffscreenCanvasRenderingContext2D>(m_context)) {
386 ASSERT(isOffscreen)((void)0);
387 return Inspector::Protocol::Canvas::ContextType::OffscreenCanvas2D;
388 }
389#endif
390 if (is<ImageBitmapRenderingContext>(m_context)) {
391 if (isOffscreen)
392 return Inspector::Protocol::Canvas::ContextType::OffscreenBitmapRenderer;
393 return Inspector::Protocol::Canvas::ContextType::BitmapRenderer;
394 }
395#if ENABLE(WEBGL)(defined 1 && 1)
396 if (is<WebGLRenderingContext>(m_context)) {
397 if (isOffscreen)
398 return Inspector::Protocol::Canvas::ContextType::OffscreenWebGL;
399 return Inspector::Protocol::Canvas::ContextType::WebGL;
400 }
401 if (is<WebGL2RenderingContext>(m_context)) {
402 if (isOffscreen)
403 return Inspector::Protocol::Canvas::ContextType::OffscreenWebGL2;
404 return Inspector::Protocol::Canvas::ContextType::WebGL2;
405 }
406#endif
407
408 ASSERT_NOT_REACHED()((void)0);
409 return Inspector::Protocol::Canvas::ContextType::Canvas2D;
410 }();
411
412 const auto& size = m_context->canvasBase().size();
413
414 auto canvas = Inspector::Protocol::Canvas::Canvas::create()
415 .setCanvasId(m_identifier)
416 .setContextType(contextType)
417 .setWidth(size.width())
418 .setHeight(size.height())
419 .release();
420
421 if (RefPtr node = canvasElement()) {
422 String cssCanvasName = node->document().nameForCSSCanvasElement(*node);
423 if (!cssCanvasName.isEmpty())
424 canvas->setCssCanvasName(cssCanvasName);
425
426 // FIXME: <https://webkit.org/b/178282> Web Inspector: send a DOM node with each Canvas payload and eliminate Canvas.requestNode
427 }
428
429 if (auto attributes = buildObjectForCanvasContextAttributes(m_context.get()))
430 canvas->setContextAttributes(attributes.releaseNonNull());
431
432 if (size_t memoryCost = m_context->memoryCost())
433 canvas->setMemoryCost(memoryCost);
434
435 if (captureBacktrace) {
436 auto stackTrace = Inspector::createScriptCallStack(JSExecState::currentState());
437 canvas->setStackTrace(stackTrace->buildInspectorObject());
438 }
439
440 return canvas;
441}
442
443Ref<Inspector::Protocol::Recording::Recording> InspectorCanvas::releaseObjectForRecording()
444{
445 ASSERT(!m_currentActions)((void)0);
446 ASSERT(!m_lastRecordedAction)((void)0);
447 ASSERT(!m_frames)((void)0);
448
449 // FIXME: <https://webkit.org/b/201651> Web Inspector: Canvas: support canvas recordings for WebGPUDevice
450
451 bool isOffscreen = false;
452#if ENABLE(OFFSCREEN_CANVAS)(defined 1 && 1)
453 if (is<OffscreenCanvas>(m_context->canvasBase()))
454 isOffscreen = true;
455#endif
456
457 Inspector::Protocol::Recording::Type type;
458 if (is<CanvasRenderingContext2D>(m_context)) {
459 ASSERT(!isOffscreen)((void)0);
460 type = Inspector::Protocol::Recording::Type::Canvas2D;
461#if ENABLE(OFFSCREEN_CANVAS)(defined 1 && 1)
462 } else if (is<OffscreenCanvasRenderingContext2D>(m_context)) {
463 ASSERT(isOffscreen)((void)0);
464 type = Inspector::Protocol::Recording::Type::OffscreenCanvas2D;
465#endif
466 } else if (is<ImageBitmapRenderingContext>(m_context)) {
467 type = isOffscreen ? Inspector::Protocol::Recording::Type::OffscreenCanvasBitmapRenderer : Inspector::Protocol::Recording::Type::CanvasBitmapRenderer;
468#if ENABLE(WEBGL)(defined 1 && 1)
469 } else if (is<WebGLRenderingContext>(m_context)) {
470 type = isOffscreen ? Inspector::Protocol::Recording::Type::OffscreenCanvasWebGL : Inspector::Protocol::Recording::Type::CanvasWebGL;
471 } else if (is<WebGL2RenderingContext>(m_context)) {
472 type = isOffscreen ? Inspector::Protocol::Recording::Type::OffscreenCanvasWebGL2 : Inspector::Protocol::Recording::Type::CanvasWebGL2;
473#endif
474 } else {
475 ASSERT_NOT_REACHED()((void)0);
476 type = Inspector::Protocol::Recording::Type::Canvas2D;
477 }
478
479 auto recording = Inspector::Protocol::Recording::Recording::create()
480 .setVersion(Inspector::Protocol::Recording::VERSION)
481 .setType(type)
482 .setInitialState(m_initialState.releaseNonNull())
483 .setData(m_serializedDuplicateData.releaseNonNull())
484 .release();
485
486 if (!m_recordingName.isEmpty())
487 recording->setName(m_recordingName);
488
489 resetRecordingData();
490
491 return recording;
492}
493
494Inspector::Protocol::ErrorStringOr<String> InspectorCanvas::getContentAsDataURL(CanvasRenderingContext& context)
495{
496 auto surfaceBuffer = context.compositingResultsNeedUpdating() ? CanvasRenderingContext::SurfaceBuffer::DrawingBuffer : CanvasRenderingContext::SurfaceBuffer::DisplayBuffer;
497 return encodeDataURL(context.surfaceBufferToImageBuffer(surfaceBuffer), "image/png"_s);
498}
499
500void InspectorCanvas::appendActionSnapshotIfNeeded()
501{
502 if (!m_lastRecordedAction)
503 return;
504
505 if (m_contentChanged) {
506 m_bufferUsed -= m_lastRecordedAction->memoryCost();
507 if (auto content = getContentAsDataURL())
508 m_lastRecordedAction->addItem(indexForData(*content));
509
510 m_bufferUsed += m_lastRecordedAction->memoryCost();
511 }
512
513 m_lastRecordedAction = nullptr;
514 m_contentChanged = false;
515}
516
517int InspectorCanvas::indexForData(DuplicateDataVariant data)
518{
519 size_t index = m_indexedDuplicateData.findIf([&] (auto item) {
520 if (data == item)
521 return true;
522
523 auto stackTraceA = std::get_if<Ref<ScriptCallStack>>(&data);
524 auto stackTraceB = std::get_if<Ref<ScriptCallStack>>(&item);
525 if (stackTraceA && stackTraceB)
526 return (*stackTraceA)->isEqual(stackTraceB->ptr());
527
528 auto parentStackTraceA = std::get_if<Ref<AsyncStackTrace>>(&data);
529 auto parentStackTraceB = std::get_if<Ref<AsyncStackTrace>>(&item);
530 if (parentStackTraceA && parentStackTraceB)
531 return parentStackTraceA->ptr() == parentStackTraceB->ptr();
532
533 return false;
534 });
535 if (index != notFound) {
536 ASSERT(index < static_cast<size_t>(std::numeric_limits<int>::max()))((void)0);
537 return static_cast<int>(index);
538 }
539
540 if (!m_serializedDuplicateData)
541 m_serializedDuplicateData = JSON::ArrayOf<JSON::Value>::create();
542
543 RefPtr<JSON::Value> item;
544 WTF::switchOn(data,
545 [&](const Ref<HTMLImageElement>& imageElement) {
546 String dataURL = "data:,"_s;
547
548 if (CachedImage* cachedImage = imageElement->cachedImage()) {
Local variable 'cachedImage' is uncounted and unsafe
549 RefPtr<Image> image = cachedImage->image();
550 if (image && image != &Image::nullImage()) {
551 dataURL = encodeDataURL(image->currentNativeImage(), "image/png"_s);
552 }
553 }
554
555 index = indexForData(dataURL);
556 },
557#if ENABLE(VIDEO)(defined 1 && 1)
558 [&](Ref<HTMLVideoElement>& videoElement) {
559 unsigned videoWidth = videoElement->videoWidth();
560 unsigned videoHeight = videoElement->videoHeight();
561 RefPtr imageBuffer = ImageBuffer::create(FloatSize(videoWidth, videoHeight), RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), PixelFormat::BGRA8);
562 if (imageBuffer)
563 videoElement->paintCurrentFrameInContext(imageBuffer->context(), FloatRect(0, 0, videoWidth, videoHeight));
564 index = indexForData(encodeDataURL(WTF::move(imageBuffer), "image/png"_s, std::nullopt));
565 },
566#endif
567 [&](Ref<HTMLCanvasElement>& canvasElement) {
568 String dataURL = "data:,"_s;
569
570 ExceptionOr<UncachedString> result = canvasElement->toDataURL("image/png"_s);
571 if (!result.hasException())
572 dataURL = result.releaseReturnValue().string;
573
574 index = indexForData(dataURL);
575 },
576 [&](Ref<CanvasGradient>& canvasGradient) { item = buildArrayForCanvasGradient(canvasGradient); },
577 [&](Ref<CanvasPattern>& canvasPattern) { item = buildArrayForCanvasPattern(canvasPattern); },
578 [&](Ref<ImageData>& imageData) { item = buildArrayForImageData(imageData); },
579 [&](Ref<ImageBitmap>& imageBitmap) {
580 index = indexForData(encodeDataURL(imageBitmap->buffer(), "image/png"_s));
581 },
582 [&](Ref<ScriptCallStack>& scriptCallStack) {
583 auto stackTrace = JSON::ArrayOf<JSON::Value>::create();
584
585 auto callFrames = JSON::ArrayOf<double>::create();
586 for (size_t i = 0; i < scriptCallStack->size(); ++i)
587 callFrames->addItem(indexForData(scriptCallStack->at(i)));
588 stackTrace->addItem(WTF::move(callFrames));
589
590 stackTrace->addItem(/* topCallFrameIsBoundary */ false);
591
592 stackTrace->addItem(scriptCallStack->truncated());
593
594 if (RefPtr parentStackTrace = scriptCallStack->parentStackTrace())
595 stackTrace->addItem(indexForData(parentStackTrace.releaseNonNull()));
596
597 item = WTF::move(stackTrace);
598 },
599 [&](const Ref<AsyncStackTrace>& parentStackTrace) {
600 auto stackTrace = JSON::ArrayOf<JSON::Value>::create();
601
602 auto callFrames = JSON::ArrayOf<double>::create();
603 for (size_t i = 0; i < parentStackTrace->size(); ++i)
604 callFrames->addItem(indexForData(parentStackTrace->at(i)));
605 stackTrace->addItem(WTF::move(callFrames));
606
607 stackTrace->addItem(parentStackTrace->topCallFrameIsBoundary());
608
609 stackTrace->addItem(parentStackTrace->truncated());
610
611 if (RefPtr grandparentStackTrace = parentStackTrace->parentStackTrace())
612 stackTrace->addItem(indexForData(grandparentStackTrace.releaseNonNull()));
613
614 item = WTF::move(stackTrace);
615 },
616 [&](const Ref<CSSStyleImageValue>& cssImageValue) {
617 String dataURL = "data:,"_s;
618
619 if (auto* cachedImage = cssImageValue->image()) {
620 RefPtr image = cachedImage->image();
621 if (image && image != &Image::nullImage())
622 dataURL = encodeDataURL(image->currentNativeImage(), "image/png"_s);
623 }
624
625 index = indexForData(dataURL);
626 },
627 [&](const ScriptCallFrame& scriptCallFrame) {
628 auto array = JSON::ArrayOf<double>::create();
629 array->addItem(indexForData(scriptCallFrame.functionName()));
630 array->addItem(indexForData(scriptCallFrame.sourceURL()));
631 array->addItem(static_cast<int>(scriptCallFrame.lineNumber()));
632 array->addItem(static_cast<int>(scriptCallFrame.columnNumber()));
633 item = WTF::move(array);
634 },
635#if ENABLE(OFFSCREEN_CANVAS)(defined 1 && 1)
636 [&](const Ref<OffscreenCanvas> offscreenCanvas) {
637 String dataURL = "data:,"_s;
638 if (offscreenCanvas->originClean())
639 dataURL = encodeDataURL(offscreenCanvas->makeRenderingResultsAvailable(), "image/png"_s);
640 index = indexForData(dataURL);
641 },
642#endif
643 [&](const String& value) { item = JSON::Value::create(value); }
644 );
645
646 if (item) {
647 m_bufferUsed += item->memoryCost();
648 m_serializedDuplicateData->addItem(item.releaseNonNull());
649
650 m_indexedDuplicateData.append(data);
651 index = m_indexedDuplicateData.size() - 1;
652 }
653
654 ASSERT(index < static_cast<size_t>(std::numeric_limits<int>::max()))((void)0);
655 return static_cast<int>(index);
656}
657
658Ref<JSON::Value> InspectorCanvas::valueIndexForData(DuplicateDataVariant data)
659{
660 return JSON::Value::create(indexForData(data));
661}
662
663String InspectorCanvas::stringIndexForKey(const String& key)
664{
665 return String::number(indexForData(key));
666}
667
668static Ref<JSON::ArrayOf<double>> buildArrayForAffineTransform(const AffineTransform& affineTransform)
669{
670 auto array = JSON::ArrayOf<double>::create();
671 array->addItem(affineTransform.a());
672 array->addItem(affineTransform.b());
673 array->addItem(affineTransform.c());
674 array->addItem(affineTransform.d());
675 array->addItem(affineTransform.e());
676 array->addItem(affineTransform.f());
677 return array;
678}
679
680Ref<Inspector::Protocol::Recording::InitialState> InspectorCanvas::buildInitialState()
681{
682 // FIXME: <https://webkit.org/b/201651> Web Inspector: Canvas: support canvas recordings for WebGPUDevice
683
684 auto initialStatePayload = Inspector::Protocol::Recording::InitialState::create().release();
685
686 auto attributesPayload = JSON::Object::create();
687 attributesPayload->setInteger("width"_s, m_context->canvasBase().width());
688 attributesPayload->setInteger("height"_s, m_context->canvasBase().height());
689
690 auto statesPayload = JSON::ArrayOf<JSON::Object>::create();
691
692 auto parametersPayload = JSON::ArrayOf<JSON::Value>::create();
693
694 if (RefPtr context2d = dynamicDowncast<CanvasRenderingContext2DBase>(m_context.get())) {
695 for (auto& state : context2d->stateStack()) {
696 auto statePayload = JSON::Object::create();
697
698 statePayload->setArray(stringIndexForKey("setTransform"_s), buildArrayForAffineTransform(state.transform));
699 statePayload->setDouble(stringIndexForKey("globalAlpha"_s), state.globalAlpha);
700 statePayload->setInteger(stringIndexForKey("globalCompositeOperation"_s), indexForData(state.globalCompositeOperationString()));
701 statePayload->setDouble(stringIndexForKey("lineWidth"_s), state.lineWidth);
702 statePayload->setInteger(stringIndexForKey("lineCap"_s), indexForData(convertEnumerationToString(state.canvasLineCap())));
703 statePayload->setInteger(stringIndexForKey("lineJoin"_s), indexForData(convertEnumerationToString(state.canvasLineJoin())));
704 statePayload->setDouble(stringIndexForKey("miterLimit"_s), state.miterLimit);
705 statePayload->setDouble(stringIndexForKey("shadowOffsetX"_s), state.shadowOffset.width());
706 statePayload->setDouble(stringIndexForKey("shadowOffsetY"_s), state.shadowOffset.height());
707 statePayload->setDouble(stringIndexForKey("shadowBlur"_s), state.shadowBlur);
708 statePayload->setInteger(stringIndexForKey("shadowColor"_s), indexForData(serializationForHTML(state.shadowColor)));
709
710 // The parameter to `setLineDash` is itself an array, so we need to wrap the parameters
711 // list in an array to allow spreading.
712 auto setLineDash = JSON::ArrayOf<JSON::Value>::create();
713 setLineDash->addItem(buildArrayForVector(state.lineDash));
714 statePayload->setArray(stringIndexForKey("setLineDash"_s), WTF::move(setLineDash));
715
716 statePayload->setDouble(stringIndexForKey("lineDashOffset"_s), state.lineDashOffset);
717 statePayload->setInteger(stringIndexForKey("font"_s), indexForData(state.fontString()));
718 statePayload->setInteger(stringIndexForKey("textAlign"_s), indexForData(convertEnumerationToString(state.canvasTextAlign())));
719 statePayload->setInteger(stringIndexForKey("textBaseline"_s), indexForData(convertEnumerationToString(state.canvasTextBaseline())));
720 statePayload->setInteger(stringIndexForKey("direction"_s), indexForData(convertEnumerationToString(state.direction)));
721
722 int strokeStyleIndex;
723 if (RefPtr canvasGradient = state.strokeStyle.canvasGradient())
724 strokeStyleIndex = indexForData(canvasGradient.releaseNonNull());
725 else if (RefPtr canvasPattern = state.strokeStyle.canvasPattern())
726 strokeStyleIndex = indexForData(canvasPattern.releaseNonNull());
727 else
728 strokeStyleIndex = indexForData(state.strokeStyle.colorString());
729 statePayload->setInteger(stringIndexForKey("strokeStyle"_s), strokeStyleIndex);
730
731 int fillStyleIndex;
732 if (RefPtr canvasGradient = state.fillStyle.canvasGradient())
733 fillStyleIndex = indexForData(canvasGradient.releaseNonNull());
734 else if (RefPtr canvasPattern = state.fillStyle.canvasPattern())
735 fillStyleIndex = indexForData(canvasPattern.releaseNonNull());
736 else
737 fillStyleIndex = indexForData(state.fillStyle.colorString());
738 statePayload->setInteger(stringIndexForKey("fillStyle"_s), fillStyleIndex);
739
740 statePayload->setBoolean(stringIndexForKey("imageSmoothingEnabled"_s), state.imageSmoothingEnabled);
741 statePayload->setInteger(stringIndexForKey("imageSmoothingQuality"_s), indexForData(convertEnumerationToString(state.imageSmoothingQuality)));
742
743 // FIXME: This is wrong: it will repeat the context's current path for every level in the stack, ignoring saved paths.
744 auto setPath = JSON::ArrayOf<JSON::Value>::create();
745 setPath->addItem(indexForData(buildStringFromPath(context2d->getPath()->path())));
746 statePayload->setArray(stringIndexForKey("setPath"_s), WTF::move(setPath));
747
748 statesPayload->addItem(WTF::move(statePayload));
749 }
750 }
751
752 if (auto contextAttributes = buildObjectForCanvasContextAttributes(m_context.get()))
753 parametersPayload->addItem(contextAttributes.releaseNonNull());
754
755 initialStatePayload->setAttributes(WTF::move(attributesPayload));
756
757 if (statesPayload->length())
758 initialStatePayload->setStates(WTF::move(statesPayload));
759
760 if (parametersPayload->length())
761 initialStatePayload->setParameters(WTF::move(parametersPayload));
762
763 if (auto content = getContentAsDataURL())
764 initialStatePayload->setContent(*content);
765
766 return initialStatePayload;
767}
768
769Ref<JSON::ArrayOf<JSON::Value>> InspectorCanvas::buildAction(String&& name, InspectorCanvasProcessedArguments&& arguments)
770{
771 auto action = JSON::ArrayOf<JSON::Value>::create();
772 action->addItem(indexForData(WTF::move(name)));
773
774 auto parametersData = JSON::ArrayOf<JSON::Value>::create();
775 auto swizzleTypes = JSON::ArrayOf<int>::create();
776 for (auto&& argument : WTF::move(arguments)) {
777 if (!argument)
778 continue;
779
780 parametersData->addItem(argument->value.copyRef());
781 swizzleTypes->addItem(static_cast<int>(argument->swizzleType));
782 }
783 action->addItem(WTF::move(parametersData));
784 action->addItem(WTF::move(swizzleTypes));
785
786 auto stackTrace = Inspector::createScriptCallStack(JSExecState::currentState());
787 action->addItem(indexForData(WTF::move(stackTrace)));
788
789 return action;
790}
791
792Ref<JSON::ArrayOf<JSON::Value>> InspectorCanvas::buildArrayForCanvasGradient(const CanvasGradient& canvasGradient)
793{
794 ASCIILiteral type = "linear-gradient"_s;
795 auto parameters = JSON::ArrayOf<double>::create();
796 WTF::switchOn(canvasGradient.gradient().data(),
797 [&] (const Gradient::LinearData& data) {
798 parameters->addItem(data.point0.x());
799 parameters->addItem(data.point0.y());
800 parameters->addItem(data.point1.x());
801 parameters->addItem(data.point1.y());
802 },
803 [&] (const Gradient::RadialData& data) {
804 type = "radial-gradient"_s;
805 parameters->addItem(data.point0.x());
806 parameters->addItem(data.point0.y());
807 parameters->addItem(data.startRadius);
808 parameters->addItem(data.point1.x());
809 parameters->addItem(data.point1.y());
810 parameters->addItem(data.endRadius);
811 },
812 [&] (const Gradient::ConicData& data) {
813 type = "conic-gradient"_s;
814 parameters->addItem(data.point0.x());
815 parameters->addItem(data.point0.y());
816 parameters->addItem(data.angleRadians);
817 }
818 );
819
820 auto stops = JSON::ArrayOf<JSON::Value>::create();
821 for (auto& colorStop : canvasGradient.gradient().stops()) {
822 auto stop = JSON::ArrayOf<JSON::Value>::create();
823 stop->addItem(colorStop.offset);
824 stop->addItem(indexForData(serializationForCSS(colorStop.color)));
825 stops->addItem(WTF::move(stop));
826 }
827
828 auto array = JSON::ArrayOf<JSON::Value>::create();
829 array->addItem(indexForData(type));
830 array->addItem(WTF::move(parameters));
831 array->addItem(WTF::move(stops));
832 return array;
833}
834
835Ref<JSON::ArrayOf<JSON::Value>> InspectorCanvas::buildArrayForCanvasPattern(const CanvasPattern& canvasPattern)
836{
837 String repeat;
838 bool repeatX = canvasPattern.pattern().repeatX();
839 bool repeatY = canvasPattern.pattern().repeatY();
840 if (repeatX && repeatY)
841 repeat = "repeat"_s;
842 else if (repeatX && !repeatY)
843 repeat = "repeat-x"_s;
844 else if (!repeatX && repeatY)
845 repeat = "repeat-y"_s;
846 else
847 repeat = "no-repeat"_s;
848
849 auto array = JSON::ArrayOf<JSON::Value>::create();
850 array->addItem(indexForData(encodeDataURL(canvasPattern.pattern().tileImageBuffer(), "image/png"_s)));
851 array->addItem(indexForData(repeat));
852 return array;
853}
854
855Ref<JSON::ArrayOf<JSON::Value>> InspectorCanvas::buildArrayForImageData(const ImageData& imageData)
856{
857 auto array = JSON::ArrayOf<JSON::Value>::create();
858 array->addItem(imageData.data().copyToJSONArray());
859 array->addItem(imageData.width());
860 array->addItem(imageData.height());
861 return array;
862}
863
864} // namespace WebCore
865