Web Inspector: Canvas: show WebGPU shader pipelines
[WebKit-https.git] / Source / WebCore / inspector / agents / InspectorCanvasAgent.cpp
1 /*
2  * Copyright (C) 2017-2019 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "InspectorCanvasAgent.h"
28
29 #include "ActiveDOMCallbackMicrotask.h"
30 #include "CanvasRenderingContext.h"
31 #include "CanvasRenderingContext2D.h"
32 #include "Document.h"
33 #include "Element.h"
34 #include "Frame.h"
35 #include "HTMLCanvasElement.h"
36 #include "ImageBitmapRenderingContext.h"
37 #include "InspectorDOMAgent.h"
38 #include "InspectorShaderProgram.h"
39 #include "InstrumentingAgents.h"
40 #include "JSExecState.h"
41 #include "Microtasks.h"
42 #include "OffscreenCanvas.h"
43 #include "ScriptState.h"
44 #include "StringAdaptors.h"
45 #include <JavaScriptCore/IdentifiersFactory.h>
46 #include <JavaScriptCore/InjectedScript.h>
47 #include <JavaScriptCore/InjectedScriptManager.h>
48 #include <JavaScriptCore/InspectorProtocolObjects.h>
49 #include <JavaScriptCore/JSCInlines.h>
50 #include <wtf/HashMap.h>
51 #include <wtf/HashSet.h>
52 #include <wtf/Lock.h>
53 #include <wtf/Optional.h>
54 #include <wtf/RefPtr.h>
55 #include <wtf/Vector.h>
56 #include <wtf/text/WTFString.h>
57
58 #if ENABLE(WEBGL)
59 #include "WebGLProgram.h"
60 #include "WebGLRenderingContext.h"
61 #include "WebGLRenderingContextBase.h"
62 #endif
63
64 #if ENABLE(WEBGL2)
65 #include "WebGL2RenderingContext.h"
66 #endif
67
68 #if ENABLE(WEBGPU)
69 #include "GPUCanvasContext.h"
70 #include "WebGPUComputePipeline.h"
71 #include "WebGPUDevice.h"
72 #include "WebGPUPipeline.h"
73 #include "WebGPURenderPipeline.h"
74 #include "WebGPUSwapChain.h"
75 #endif
76
77 namespace WebCore {
78
79 using namespace Inspector;
80
81 InspectorCanvasAgent::InspectorCanvasAgent(PageAgentContext& context)
82     : InspectorAgentBase("Canvas"_s, context)
83     , m_frontendDispatcher(makeUnique<Inspector::CanvasFrontendDispatcher>(context.frontendRouter))
84     , m_backendDispatcher(Inspector::CanvasBackendDispatcher::create(context.backendDispatcher, this))
85     , m_injectedScriptManager(context.injectedScriptManager)
86     , m_inspectedPage(context.inspectedPage)
87     , m_canvasDestroyedTimer(*this, &InspectorCanvasAgent::canvasDestroyedTimerFired)
88     , m_programDestroyedTimer(*this, &InspectorCanvasAgent::programDestroyedTimerFired)
89 {
90 }
91
92 InspectorCanvasAgent::~InspectorCanvasAgent() = default;
93
94 void InspectorCanvasAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
95 {
96 }
97
98 void InspectorCanvasAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
99 {
100     ErrorString ignored;
101     disable(ignored);
102 }
103
104 void InspectorCanvasAgent::discardAgent()
105 {
106     reset();
107 }
108
109 void InspectorCanvasAgent::enable(ErrorString&)
110 {
111     if (m_instrumentingAgents.inspectorCanvasAgent() == this)
112         return;
113
114     m_instrumentingAgents.setInspectorCanvasAgent(this);
115
116     const auto existsInCurrentPage = [&] (ScriptExecutionContext* scriptExecutionContext) {
117         if (!is<Document>(scriptExecutionContext))
118             return false;
119
120         // FIXME: <https://webkit.org/b/168475> Web Inspector: Correctly display iframe's WebSockets
121         auto* document = downcast<Document>(scriptExecutionContext);
122         return document->page() == &m_inspectedPage;
123     };
124
125     {
126         LockHolder lock(CanvasRenderingContext::instancesMutex());
127         for (auto* context : CanvasRenderingContext::instances(lock)) {
128 #if ENABLE(WEBGPU)
129             // The actual "context" for WebGPU is the `WebGPUDevice`, not the <canvas>.
130             if (is<GPUCanvasContext>(context))
131                 continue;
132 #endif
133
134             if (existsInCurrentPage(context->canvasBase().scriptExecutionContext()))
135                 bindCanvas(*context, false);
136         }
137     }
138
139 #if ENABLE(WEBGPU)
140     {
141         LockHolder lock(WebGPUDevice::instancesMutex());
142         for (auto* device : WebGPUDevice::instances(lock)) {
143             if (existsInCurrentPage(device->scriptExecutionContext()))
144                 bindCanvas(*device, false);
145         }
146     }
147 #endif
148
149 #if ENABLE(WEBGL)
150     {
151         LockHolder lock(WebGLProgram::instancesMutex());
152         for (auto& [program, contextWebGLBase] : WebGLProgram::instances(lock)) {
153             if (contextWebGLBase && existsInCurrentPage(contextWebGLBase->canvasBase().scriptExecutionContext()))
154                 didCreateWebGLProgram(*contextWebGLBase, *program);
155         }
156     }
157 #endif
158
159 #if ENABLE(WEBGPU)
160     {
161         LockHolder lock(WebGPUPipeline::instancesMutex());
162         for (auto& [pipeline, device] : WebGPUPipeline::instances(lock)) {
163             if (device && existsInCurrentPage(device->scriptExecutionContext()) && pipeline->isValid())
164                 didCreateWebGPUPipeline(*device, *pipeline);
165         }
166     }
167 #endif
168 }
169
170 void InspectorCanvasAgent::disable(ErrorString&)
171 {
172     m_instrumentingAgents.setInspectorCanvasAgent(nullptr);
173
174     reset();
175
176     m_recordingAutoCaptureFrameCount = WTF::nullopt;
177 }
178
179 void InspectorCanvasAgent::requestNode(ErrorString& errorString, const String& canvasId, int* nodeId)
180 {
181     auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
182     if (!inspectorCanvas)
183         return;
184
185     auto* node = inspectorCanvas->canvasElement();
186     if (!node) {
187         errorString = "Missing element of canvas for given canvasId"_s;
188         return;
189     }
190
191     int documentNodeId = m_instrumentingAgents.inspectorDOMAgent()->boundNodeId(&node->document());
192     if (!documentNodeId) {
193         errorString = "Document must have been requested"_s;
194         return;
195     }
196
197     *nodeId = m_instrumentingAgents.inspectorDOMAgent()->pushNodeToFrontend(errorString, documentNodeId, node);
198 }
199
200 void InspectorCanvasAgent::requestContent(ErrorString& errorString, const String& canvasId, String* content)
201 {
202     auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
203     if (!inspectorCanvas)
204         return;
205
206     *content = inspectorCanvas->getCanvasContentAsDataURL(errorString);
207 }
208
209 void InspectorCanvasAgent::requestClientNodes(ErrorString& errorString, const String& canvasId, RefPtr<JSON::ArrayOf<int>>& clientNodeIds)
210 {
211     auto* domAgent = m_instrumentingAgents.inspectorDOMAgent();
212     if (!domAgent) {
213         errorString = "DOM domain must be enabled"_s;
214         return;
215     }
216
217     auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
218     if (!inspectorCanvas)
219         return;
220
221     clientNodeIds = JSON::ArrayOf<int>::create();
222     for (auto& clientNode : inspectorCanvas->clientNodes()) {
223         if (auto documentNodeId = domAgent->boundNodeId(&clientNode->document()))
224             clientNodeIds->addItem(domAgent->pushNodeToFrontend(errorString, documentNodeId, clientNode));
225     }
226 }
227
228 void InspectorCanvasAgent::resolveContext(ErrorString& errorString, const String& canvasId, const String* objectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result)
229 {
230     auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
231     if (!inspectorCanvas)
232         return;
233
234     auto* state = inspectorCanvas->scriptExecutionContext()->execState();
235     auto injectedScript = m_injectedScriptManager.injectedScriptFor(state);
236     ASSERT(!injectedScript.hasNoValue());
237
238     JSC::JSValue value = inspectorCanvas->resolveContext(state);
239
240     if (!value) {
241         ASSERT_NOT_REACHED();
242         errorString = "Internal error: unknown context of canvas for given canvasId"_s;
243         return;
244     }
245
246     String objectGroupName = objectGroup ? *objectGroup : String();
247     result = injectedScript.wrapObject(value, objectGroupName);
248 }
249
250 void InspectorCanvasAgent::setRecordingAutoCaptureFrameCount(ErrorString&, int count)
251 {
252     if (count > 0)
253         m_recordingAutoCaptureFrameCount = count;
254     else
255         m_recordingAutoCaptureFrameCount = WTF::nullopt;
256 }
257
258 void InspectorCanvasAgent::startRecording(ErrorString& errorString, const String& canvasId, const int* frameCount, const int* memoryLimit)
259 {
260     auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
261     if (!inspectorCanvas)
262         return;
263
264     // FIXME: <https://webkit.org/b/201651> Web Inspector: Canvas: support canvas recordings for WebGPUDevice
265
266     auto* context = inspectorCanvas->canvasContext();
267     if (!context)
268         return;
269
270     if (context->callTracingActive()) {
271         errorString = "Already recording canvas"_s;
272         return;
273     }
274
275     RecordingOptions recordingOptions;
276     if (frameCount)
277         recordingOptions.frameCount = *frameCount;
278     if (memoryLimit)
279         recordingOptions.memoryLimit = *memoryLimit;
280     startRecording(*inspectorCanvas, Inspector::Protocol::Recording::Initiator::Frontend, WTFMove(recordingOptions));
281 }
282
283 void InspectorCanvasAgent::stopRecording(ErrorString& errorString, const String& canvasId)
284 {
285     auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
286     if (!inspectorCanvas)
287         return;
288
289     // FIXME: <https://webkit.org/b/201651> Web Inspector: Canvas: support canvas recordings for WebGPUDevice
290
291     auto* context = inspectorCanvas->canvasContext();
292     if (!context)
293         return;
294
295     if (!context->callTracingActive()) {
296         errorString = "Not recording canvas"_s;
297         return;
298     }
299
300     didFinishRecordingCanvasFrame(*context, true);
301 }
302
303 void InspectorCanvasAgent::requestShaderSource(ErrorString& errorString, const String& programId, const String& shaderTypeString, String* outSource)
304 {
305     auto inspectorProgram = assertInspectorProgram(errorString, programId);
306     if (!inspectorProgram)
307         return;
308
309     auto shaderType = Inspector::Protocol::InspectorHelpers::parseEnumValueFromString<Inspector::Protocol::Canvas::ShaderType>(shaderTypeString);
310     if (!shaderType) {
311         errorString = makeString("Unknown shaderType: "_s, shaderTypeString);
312         return;
313     }
314
315     auto source = inspectorProgram->requestShaderSource(shaderType.value());
316     if (!source) {
317         errorString = "Missing shader of given shaderType for given programId"_s;
318         return;
319     }
320
321     *outSource = source;
322 }
323
324 void InspectorCanvasAgent::updateShader(ErrorString& errorString, const String& programId, const String& shaderTypeString, const String& source)
325 {
326     auto inspectorProgram = assertInspectorProgram(errorString, programId);
327     if (!inspectorProgram)
328         return;
329
330     auto shaderType = Inspector::Protocol::InspectorHelpers::parseEnumValueFromString<Inspector::Protocol::Canvas::ShaderType>(shaderTypeString);
331     if (!shaderType) {
332         errorString = makeString("Unknown shaderType: "_s, shaderTypeString);
333         return;
334     }
335
336     if (!inspectorProgram->updateShader(shaderType.value(), source))
337         errorString = "Failed to update shader of given shaderType for given programId"_s;
338 }
339
340 void InspectorCanvasAgent::setShaderProgramDisabled(ErrorString& errorString, const String& programId, bool disabled)
341 {
342     auto inspectorProgram = assertInspectorProgram(errorString, programId);
343     if (!inspectorProgram)
344         return;
345
346     inspectorProgram->setDisabled(disabled);
347 }
348
349 void InspectorCanvasAgent::setShaderProgramHighlighted(ErrorString& errorString, const String& programId, bool highlighted)
350 {
351     auto inspectorProgram = assertInspectorProgram(errorString, programId);
352     if (!inspectorProgram)
353         return;
354
355     inspectorProgram->setHighlighted(highlighted);
356 }
357
358 void InspectorCanvasAgent::frameNavigated(Frame& frame)
359 {
360     if (frame.isMainFrame()) {
361         reset();
362         return;
363     }
364
365     Vector<InspectorCanvas*> inspectorCanvases;
366     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
367         if (auto* canvasElement = inspectorCanvas->canvasElement()) {
368             if (canvasElement->document().frame() == &frame)
369                 inspectorCanvases.append(inspectorCanvas.get());
370         }
371     }
372
373     for (auto* inspectorCanvas : inspectorCanvases)
374         unbindCanvas(*inspectorCanvas);
375 }
376
377 void InspectorCanvasAgent::didChangeCSSCanvasClientNodes(CanvasBase& canvasBase)
378 {
379     auto* context = canvasBase.renderingContext();
380     if (!context) {
381         ASSERT_NOT_REACHED();
382         return;
383     }
384
385     auto inspectorCanvas = findInspectorCanvas(*context);
386     ASSERT(inspectorCanvas);
387     if (!inspectorCanvas)
388         return;
389
390     m_frontendDispatcher->clientNodesChanged(inspectorCanvas->identifier());
391 }
392
393 void InspectorCanvasAgent::didCreateCanvasRenderingContext(CanvasRenderingContext& context)
394 {
395     if (findInspectorCanvas(context)) {
396         ASSERT_NOT_REACHED();
397         return;
398     }
399
400     auto& inspectorCanvas = bindCanvas(context, true);
401
402     if (m_recordingAutoCaptureFrameCount) {
403         RecordingOptions recordingOptions;
404         recordingOptions.frameCount = m_recordingAutoCaptureFrameCount.value();
405         startRecording(inspectorCanvas, Inspector::Protocol::Recording::Initiator::AutoCapture, WTFMove(recordingOptions));
406     }
407 }
408
409 void InspectorCanvasAgent::didChangeCanvasMemory(CanvasRenderingContext& context)
410 {
411     RefPtr<InspectorCanvas> inspectorCanvas;
412
413 #if ENABLE(WEBGPU)
414     if (is<GPUCanvasContext>(context)) {
415         for (auto& item : m_identifierToInspectorCanvas.values()) {
416             if (item->isDeviceForCanvasContext(context)) {
417                 inspectorCanvas = item;
418                 break;
419             }
420         }
421     }
422 #endif
423
424     if (!inspectorCanvas)
425         inspectorCanvas = findInspectorCanvas(context);
426
427     ASSERT(inspectorCanvas);
428     if (!inspectorCanvas)
429         return;
430
431     // FIXME: <https://webkit.org/b/180833> Web Inspector: support OffscreenCanvas for Canvas related operations
432
433     if (auto* node = inspectorCanvas->canvasElement())
434         m_frontendDispatcher->canvasMemoryChanged(inspectorCanvas->identifier(), node->memoryCost());
435 }
436
437 void InspectorCanvasAgent::recordCanvasAction(CanvasRenderingContext& canvasRenderingContext, const String& name, std::initializer_list<RecordCanvasActionVariant>&& parameters)
438 {
439     auto inspectorCanvas = findInspectorCanvas(canvasRenderingContext);
440     ASSERT(inspectorCanvas);
441     if (!inspectorCanvas)
442         return;
443
444     ASSERT(canvasRenderingContext.callTracingActive());
445     if (!canvasRenderingContext.callTracingActive())
446         return;
447
448     // Only enqueue a microtask for the first action of each frame. Any subsequent actions will be
449     // covered by the initial microtask until the next frame.
450     if (!inspectorCanvas->currentFrameHasData()) {
451         if (auto* scriptExecutionContext = inspectorCanvas->scriptExecutionContext()) {
452             auto& queue = MicrotaskQueue::mainThreadQueue();
453             queue.append(makeUnique<ActiveDOMCallbackMicrotask>(queue, *scriptExecutionContext, [&, protectedInspectorCanvas = inspectorCanvas.copyRef()] {
454                 if (auto* canvasElement = protectedInspectorCanvas->canvasElement()) {
455                     if (canvasElement->isDescendantOf(canvasElement->document()))
456                         return;
457                 }
458
459                 if (canvasRenderingContext.callTracingActive())
460                     didFinishRecordingCanvasFrame(canvasRenderingContext);
461             }));
462         }
463     }
464
465     inspectorCanvas->recordAction(name, WTFMove(parameters));
466
467     if (!inspectorCanvas->hasBufferSpace())
468         didFinishRecordingCanvasFrame(canvasRenderingContext, true);
469 }
470
471 void InspectorCanvasAgent::canvasChanged(CanvasBase& canvasBase, const FloatRect&)
472 {
473     auto* context = canvasBase.renderingContext();
474     if (!context)
475         return;
476
477     auto inspectorCanvas = findInspectorCanvas(*context);
478     ASSERT(inspectorCanvas);
479     if (!inspectorCanvas)
480         return;
481
482     inspectorCanvas->canvasChanged();
483 }
484
485 void InspectorCanvasAgent::canvasDestroyed(CanvasBase& canvasBase)
486 {
487     auto* context = canvasBase.renderingContext();
488     if (!context)
489         return;
490
491     auto inspectorCanvas = findInspectorCanvas(*context);
492     ASSERT(inspectorCanvas);
493     if (!inspectorCanvas)
494         return;
495
496     unbindCanvas(*inspectorCanvas);
497 }
498
499 void InspectorCanvasAgent::didFinishRecordingCanvasFrame(CanvasRenderingContext& context, bool forceDispatch)
500 {
501     if (!context.callTracingActive())
502         return;
503
504     auto inspectorCanvas = findInspectorCanvas(context);
505     ASSERT(inspectorCanvas);
506     if (!inspectorCanvas)
507         return;
508
509     if (!inspectorCanvas->hasRecordingData()) {
510         if (forceDispatch) {
511             m_frontendDispatcher->recordingFinished(inspectorCanvas->identifier(), nullptr);
512             inspectorCanvas->resetRecordingData();
513         }
514         return;
515     }
516
517     if (forceDispatch)
518         inspectorCanvas->markCurrentFrameIncomplete();
519
520     inspectorCanvas->finalizeFrame();
521     if (inspectorCanvas->currentFrameHasData())
522         m_frontendDispatcher->recordingProgress(inspectorCanvas->identifier(), inspectorCanvas->releaseFrames(), inspectorCanvas->bufferUsed());
523
524     if (!forceDispatch && !inspectorCanvas->overFrameCount())
525         return;
526
527     m_frontendDispatcher->recordingFinished(inspectorCanvas->identifier(), inspectorCanvas->releaseObjectForRecording());
528 }
529
530 void InspectorCanvasAgent::consoleStartRecordingCanvas(CanvasRenderingContext& context, JSC::ExecState& exec, JSC::JSObject* options)
531 {
532     auto inspectorCanvas = findInspectorCanvas(context);
533     ASSERT(inspectorCanvas);
534     if (!inspectorCanvas)
535         return;
536
537     RecordingOptions recordingOptions;
538     if (options) {
539         JSC::VM& vm = exec.vm();
540         if (JSC::JSValue optionSingleFrame = options->get(&exec, JSC::Identifier::fromString(vm, "singleFrame")))
541             recordingOptions.frameCount = optionSingleFrame.toBoolean(&exec) ? 1 : 0;
542         if (JSC::JSValue optionFrameCount = options->get(&exec, JSC::Identifier::fromString(vm, "frameCount")))
543             recordingOptions.frameCount = optionFrameCount.toNumber(&exec);
544         if (JSC::JSValue optionMemoryLimit = options->get(&exec, JSC::Identifier::fromString(vm, "memoryLimit")))
545             recordingOptions.memoryLimit = optionMemoryLimit.toNumber(&exec);
546         if (JSC::JSValue optionName = options->get(&exec, JSC::Identifier::fromString(vm, "name")))
547             recordingOptions.name = optionName.toWTFString(&exec);
548     }
549     startRecording(*inspectorCanvas, Inspector::Protocol::Recording::Initiator::Console, WTFMove(recordingOptions));
550 }
551
552 #if ENABLE(WEBGL)
553 void InspectorCanvasAgent::didEnableExtension(WebGLRenderingContextBase& context, const String& extension)
554 {
555     auto inspectorCanvas = findInspectorCanvas(context);
556     ASSERT(inspectorCanvas);
557     if (!inspectorCanvas)
558         return;
559
560     m_frontendDispatcher->extensionEnabled(inspectorCanvas->identifier(), extension);
561 }
562
563 void InspectorCanvasAgent::didCreateWebGLProgram(WebGLRenderingContextBase& context, WebGLProgram& program)
564 {
565     auto inspectorCanvas = findInspectorCanvas(context);
566     ASSERT(inspectorCanvas);
567     if (!inspectorCanvas)
568         return;
569
570     auto inspectorProgram = InspectorShaderProgram::create(program, *inspectorCanvas);
571     String programIdentifier = inspectorProgram->identifier();
572     m_identifierToInspectorProgram.set(programIdentifier, WTFMove(inspectorProgram));
573     m_frontendDispatcher->programCreated(inspectorCanvas->identifier(), programIdentifier, Inspector::Protocol::Canvas::ProgramType::Render);
574 }
575
576 void InspectorCanvasAgent::willDestroyWebGLProgram(WebGLProgram& program)
577 {
578     auto inspectorProgram = findInspectorProgram(program);
579     if (!inspectorProgram)
580         return;
581
582     unbindProgram(*inspectorProgram);
583 }
584
585 bool InspectorCanvasAgent::isWebGLProgramDisabled(WebGLProgram& program)
586 {
587     auto inspectorProgram = findInspectorProgram(program);
588     ASSERT(inspectorProgram);
589     if (!inspectorProgram)
590         return false;
591
592     return inspectorProgram->disabled();
593 }
594
595 bool InspectorCanvasAgent::isWebGLProgramHighlighted(WebGLProgram& program)
596 {
597     auto inspectorProgram = findInspectorProgram(program);
598     ASSERT(inspectorProgram);
599     if (!inspectorProgram)
600         return false;
601
602     return inspectorProgram->highlighted();
603 }
604 #endif
605
606 #if ENABLE(WEBGPU)
607 void InspectorCanvasAgent::didCreateWebGPUDevice(WebGPUDevice& device)
608 {
609     if (findInspectorCanvas(device)) {
610         ASSERT_NOT_REACHED();
611         return;
612     }
613
614     bindCanvas(device, true);
615 }
616
617 void InspectorCanvasAgent::willDestroyWebGPUDevice(WebGPUDevice& device)
618 {
619     auto inspectorCanvas = findInspectorCanvas(device);
620     ASSERT(inspectorCanvas);
621     if (!inspectorCanvas)
622         return;
623
624     unbindCanvas(*inspectorCanvas);
625 }
626
627 void InspectorCanvasAgent::willConfigureSwapChain(GPUCanvasContext& contextGPU, WebGPUSwapChain& newSwapChain)
628 {
629     auto notifyDeviceForSwapChain = [&] (WebGPUSwapChain& webGPUSwapChain) {
630         for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
631             if (auto* device = inspectorCanvas->deviceContext()) {
632                 if (device->device().swapChain() == webGPUSwapChain.swapChain())
633                     m_frontendDispatcher->clientNodesChanged(inspectorCanvas->identifier());
634             }
635         }
636     };
637
638     if (auto* existingSwapChain = contextGPU.swapChain())
639         notifyDeviceForSwapChain(*existingSwapChain);
640
641     notifyDeviceForSwapChain(newSwapChain);
642 }
643
644 void InspectorCanvasAgent::didCreateWebGPUPipeline(WebGPUDevice& device, WebGPUPipeline& pipeline)
645 {
646     auto inspectorCanvas = findInspectorCanvas(device);
647     ASSERT(inspectorCanvas);
648     if (!inspectorCanvas)
649         return;
650
651     ASSERT(pipeline.isValid());
652
653     auto inspectorProgram = InspectorShaderProgram::create(pipeline, *inspectorCanvas);
654     String programIdentifier = inspectorProgram->identifier();
655     m_identifierToInspectorProgram.set(programIdentifier, WTFMove(inspectorProgram));
656
657     Optional<Inspector::Protocol::Canvas::ProgramType> programType;
658     if (is<WebGPUComputePipeline>(pipeline))
659         programType = Inspector::Protocol::Canvas::ProgramType::Compute;
660     else if (is<WebGPURenderPipeline>(pipeline))
661         programType = Inspector::Protocol::Canvas::ProgramType::Render;
662     ASSERT(programType);
663
664     m_frontendDispatcher->programCreated(inspectorCanvas->identifier(), programIdentifier, programType.value());
665 }
666
667 void InspectorCanvasAgent::willDestroyWebGPUPipeline(WebGPUPipeline& pipeline)
668 {
669     auto inspectorProgram = findInspectorProgram(pipeline);
670     if (!inspectorProgram)
671         return;
672
673     unbindProgram(*inspectorProgram);
674 }
675 #endif
676
677 void InspectorCanvasAgent::startRecording(InspectorCanvas& inspectorCanvas, Inspector::Protocol::Recording::Initiator initiator, RecordingOptions&& recordingOptions)
678 {
679     auto* context = inspectorCanvas.canvasContext();
680     ASSERT(context);
681     // FIXME: <https://webkit.org/b/201651> Web Inspector: Canvas: support canvas recordings for WebGPUDevice
682
683     if (!is<CanvasRenderingContext2D>(context)
684         && !is<ImageBitmapRenderingContext>(context)
685 #if ENABLE(WEBGL)
686         && !is<WebGLRenderingContext>(context)
687 #endif
688 #if ENABLE(WEBGL2)
689         && !is<WebGL2RenderingContext>(context)
690 #endif
691     )
692         return;
693
694     if (context->callTracingActive())
695         return;
696
697     inspectorCanvas.resetRecordingData();
698     if (recordingOptions.frameCount)
699         inspectorCanvas.setFrameCount(recordingOptions.frameCount.value());
700     if (recordingOptions.memoryLimit)
701         inspectorCanvas.setBufferLimit(recordingOptions.memoryLimit.value());
702     if (recordingOptions.name)
703         inspectorCanvas.setRecordingName(recordingOptions.name.value());
704     context->setCallTracingActive(true);
705
706     m_frontendDispatcher->recordingStarted(inspectorCanvas.identifier(), initiator);
707 }
708
709 void InspectorCanvasAgent::canvasDestroyedTimerFired()
710 {
711     if (!m_removedCanvasIdentifiers.size())
712         return;
713
714     for (auto& identifier : m_removedCanvasIdentifiers)
715         m_frontendDispatcher->canvasRemoved(identifier);
716
717     m_removedCanvasIdentifiers.clear();
718 }
719
720 void InspectorCanvasAgent::programDestroyedTimerFired()
721 {
722     if (!m_removedProgramIdentifiers.size())
723         return;
724
725     for (auto& identifier : m_removedProgramIdentifiers)
726         m_frontendDispatcher->programDeleted(identifier);
727
728     m_removedProgramIdentifiers.clear();
729 }
730
731 void InspectorCanvasAgent::reset()
732 {
733     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
734         if (auto* context = inspectorCanvas->canvasContext())
735             context->canvasBase().removeObserver(*this);
736     }
737
738     m_identifierToInspectorCanvas.clear();
739     m_removedCanvasIdentifiers.clear();
740     if (m_canvasDestroyedTimer.isActive())
741         m_canvasDestroyedTimer.stop();
742
743     m_identifierToInspectorProgram.clear();
744     m_removedProgramIdentifiers.clear();
745     if (m_programDestroyedTimer.isActive())
746         m_programDestroyedTimer.stop();
747 }
748
749 InspectorCanvas& InspectorCanvasAgent::bindCanvas(CanvasRenderingContext& context, bool captureBacktrace)
750 {
751     auto inspectorCanvas = InspectorCanvas::create(context);
752     m_identifierToInspectorCanvas.set(inspectorCanvas->identifier(), inspectorCanvas.copyRef());
753
754     context.canvasBase().addObserver(*this);
755
756     m_frontendDispatcher->canvasAdded(inspectorCanvas->buildObjectForCanvas(captureBacktrace));
757
758 #if ENABLE(WEBGL)
759     if (is<WebGLRenderingContextBase>(context)) {
760         auto& contextWebGL = downcast<WebGLRenderingContextBase>(context);
761         if (Optional<Vector<String>> extensions = contextWebGL.getSupportedExtensions()) {
762             for (const String& extension : *extensions) {
763                 if (contextWebGL.extensionIsEnabled(extension))
764                     m_frontendDispatcher->extensionEnabled(inspectorCanvas->identifier(), extension);
765             }
766         }
767     }
768 #endif
769
770     return inspectorCanvas;
771 }
772
773 #if ENABLE(WEBGPU)
774 InspectorCanvas& InspectorCanvasAgent::bindCanvas(WebGPUDevice& device, bool captureBacktrace)
775 {
776     auto inspectorCanvas = InspectorCanvas::create(device);
777     m_identifierToInspectorCanvas.set(inspectorCanvas->identifier(), inspectorCanvas.copyRef());
778
779     m_frontendDispatcher->canvasAdded(inspectorCanvas->buildObjectForCanvas(captureBacktrace));
780
781     return inspectorCanvas;
782 }
783 #endif
784
785 void InspectorCanvasAgent::unbindCanvas(InspectorCanvas& inspectorCanvas)
786 {
787 #if ENABLE(WEBGL)
788     Vector<InspectorShaderProgram*> programsToRemove;
789     for (auto& inspectorProgram : m_identifierToInspectorProgram.values()) {
790         if (&inspectorProgram->canvas() == &inspectorCanvas)
791             programsToRemove.append(inspectorProgram.get());
792     }
793
794     for (auto* inspectorProgram : programsToRemove)
795         unbindProgram(*inspectorProgram);
796 #endif
797
798     if (auto* context = inspectorCanvas.canvasContext())
799         context->canvasBase().removeObserver(*this);
800
801     String identifier = inspectorCanvas.identifier();
802     m_identifierToInspectorCanvas.remove(identifier);
803
804     // This can be called in response to GC. Due to the single-process model used in WebKit1, the
805     // event must be dispatched from a timer to prevent the frontend from making JS allocations
806     // while the GC is still active.
807     m_removedCanvasIdentifiers.append(identifier);
808
809     if (!m_canvasDestroyedTimer.isActive())
810         m_canvasDestroyedTimer.startOneShot(0_s);
811 }
812
813 RefPtr<InspectorCanvas> InspectorCanvasAgent::assertInspectorCanvas(ErrorString& errorString, const String& canvasId)
814 {
815     auto inspectorCanvas = m_identifierToInspectorCanvas.get(canvasId);
816     if (!inspectorCanvas) {
817         errorString = "Missing canvas for given canvasId"_s;
818         return nullptr;
819     }
820     return inspectorCanvas;
821 }
822
823 RefPtr<InspectorCanvas> InspectorCanvasAgent::findInspectorCanvas(CanvasRenderingContext& context)
824 {
825     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
826         if (inspectorCanvas->canvasContext() == &context)
827             return inspectorCanvas;
828     }
829     return nullptr;
830 }
831
832 #if ENABLE(WEBGPU)
833 RefPtr<InspectorCanvas> InspectorCanvasAgent::findInspectorCanvas(WebGPUDevice& device)
834 {
835     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
836         if (inspectorCanvas->deviceContext() == &device)
837             return inspectorCanvas;
838     }
839     return nullptr;
840 }
841 #endif
842
843 void InspectorCanvasAgent::unbindProgram(InspectorShaderProgram& inspectorProgram)
844 {
845     String identifier = inspectorProgram.identifier();
846     m_identifierToInspectorProgram.remove(identifier);
847
848     // This can be called in response to GC. Due to the single-process model used in WebKit1, the
849     // event must be dispatched from a timer to prevent the frontend from making JS allocations
850     // while the GC is still active.
851     m_removedProgramIdentifiers.append(identifier);
852
853     if (!m_programDestroyedTimer.isActive())
854         m_programDestroyedTimer.startOneShot(0_s);
855 }
856
857 RefPtr<InspectorShaderProgram> InspectorCanvasAgent::assertInspectorProgram(ErrorString& errorString, const String& programId)
858 {
859     auto inspectorProgram = m_identifierToInspectorProgram.get(programId);
860     if (!inspectorProgram) {
861         errorString = "Missing program for given programId"_s;
862         return nullptr;
863     }
864     return inspectorProgram;
865 }
866
867 #if ENABLE(WEBGL)
868 RefPtr<InspectorShaderProgram> InspectorCanvasAgent::findInspectorProgram(WebGLProgram& program)
869 {
870     for (auto& inspectorProgram : m_identifierToInspectorProgram.values()) {
871         if (inspectorProgram->program() == &program)
872             return inspectorProgram;
873     }
874     return nullptr;
875 }
876 #endif
877
878 #if ENABLE(WEBGPU)
879 RefPtr<InspectorShaderProgram> InspectorCanvasAgent::findInspectorProgram(WebGPUPipeline& pipeline)
880 {
881     for (auto& inspectorProgram : m_identifierToInspectorProgram.values()) {
882         if (inspectorProgram->pipeline() == &pipeline)
883             return inspectorProgram;
884     }
885     return nullptr;
886 }
887 #endif
888
889 } // namespace WebCore