Web Inspector: tint all pixels drawn by shader program when hovering ShaderProgramTre...
[WebKit-https.git] / Source / WebCore / inspector / agents / InspectorCanvasAgent.cpp
1 /*
2  * Copyright (C) 2017 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 "CanvasRenderingContext.h"
30 #include "CanvasRenderingContext2D.h"
31 #include "Document.h"
32 #include "Element.h"
33 #include "Frame.h"
34 #include "ImageBitmapRenderingContext.h"
35 #include "InspectorDOMAgent.h"
36 #include "InstrumentingAgents.h"
37 #include "JSCanvasRenderingContext2D.h"
38 #include "JSImageBitmapRenderingContext.h"
39 #include "JSMainThreadExecState.h"
40 #include "MainFrame.h"
41 #include "OffscreenCanvas.h"
42 #include "ScriptState.h"
43 #include "StringAdaptors.h"
44 #include <JavaScriptCore/IdentifiersFactory.h>
45 #include <JavaScriptCore/InjectedScript.h>
46 #include <JavaScriptCore/InjectedScriptManager.h>
47 #include <JavaScriptCore/InspectorProtocolObjects.h>
48 #include <JavaScriptCore/JSCInlines.h>
49
50 #if ENABLE(WEBGL)
51 #include "JSWebGLRenderingContext.h"
52 #include "WebGLProgram.h"
53 #include "WebGLShader.h"
54 #endif
55
56 #if ENABLE(WEBGL2)
57 #include "JSWebGL2RenderingContext.h"
58 #endif
59
60 #if ENABLE(WEBGPU)
61 #include "JSWebGPURenderingContext.h"
62 #endif
63
64
65 namespace WebCore {
66
67 using namespace Inspector;
68
69 InspectorCanvasAgent::InspectorCanvasAgent(WebAgentContext& context)
70     : InspectorAgentBase(ASCIILiteral("Canvas"), context)
71     , m_frontendDispatcher(std::make_unique<Inspector::CanvasFrontendDispatcher>(context.frontendRouter))
72     , m_backendDispatcher(Inspector::CanvasBackendDispatcher::create(context.backendDispatcher, this))
73     , m_injectedScriptManager(context.injectedScriptManager)
74     , m_canvasDestroyedTimer(*this, &InspectorCanvasAgent::canvasDestroyedTimerFired)
75     , m_canvasRecordingTimer(*this, &InspectorCanvasAgent::canvasRecordingTimerFired)
76 {
77 }
78
79 void InspectorCanvasAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
80 {
81 }
82
83 void InspectorCanvasAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
84 {
85     ErrorString ignored;
86     disable(ignored);
87 }
88
89 void InspectorCanvasAgent::discardAgent()
90 {
91     clearCanvasData();
92 }
93
94 void InspectorCanvasAgent::enable(ErrorString&)
95 {
96     if (m_enabled)
97         return;
98
99     m_enabled = true;
100
101     const bool captureBacktrace = false;
102     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
103         m_frontendDispatcher->canvasAdded(inspectorCanvas->buildObjectForCanvas(m_instrumentingAgents, captureBacktrace));
104
105 #if ENABLE(WEBGL)
106         if (is<WebGLRenderingContextBase>(inspectorCanvas->context())) {
107             WebGLRenderingContextBase& contextWebGL = downcast<WebGLRenderingContextBase>(inspectorCanvas->context());
108             if (std::optional<Vector<String>> extensions = contextWebGL.getSupportedExtensions()) {
109                 for (const String& extension : *extensions) {
110                     if (contextWebGL.extensionIsEnabled(extension))
111                         m_frontendDispatcher->extensionEnabled(inspectorCanvas->identifier(), extension);
112                 }
113             }
114         }
115 #endif
116     }
117
118 #if ENABLE(WEBGL)
119     for (auto& inspectorProgram : m_identifierToInspectorProgram.values()) {
120         auto& inspectorCanvas = inspectorProgram->canvas();
121         m_frontendDispatcher->programCreated(inspectorCanvas.identifier(), inspectorProgram->identifier());
122     }
123 #endif
124 }
125
126 void InspectorCanvasAgent::disable(ErrorString&)
127 {
128     if (!m_enabled)
129         return;
130
131     if (m_canvasDestroyedTimer.isActive())
132         m_canvasDestroyedTimer.stop();
133
134     m_removedCanvasIdentifiers.clear();
135
136     if (m_canvasRecordingTimer.isActive())
137         m_canvasRecordingTimer.stop();
138
139     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values())
140         inspectorCanvas->resetRecordingData();
141
142     m_enabled = false;
143 }
144
145 void InspectorCanvasAgent::requestNode(ErrorString& errorString, const String& canvasId, int* nodeId)
146 {
147     auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
148     if (!inspectorCanvas)
149         return;
150
151     auto* node = inspectorCanvas->canvasElement();
152     if (!node) {
153         errorString = ASCIILiteral("No node for canvas");
154         return;
155     }
156
157     int documentNodeId = m_instrumentingAgents.inspectorDOMAgent()->boundNodeId(&node->document());
158     if (!documentNodeId) {
159         errorString = ASCIILiteral("Document has not been requested");
160         return;
161     }
162
163     *nodeId = m_instrumentingAgents.inspectorDOMAgent()->pushNodeToFrontend(errorString, documentNodeId, node);
164 }
165
166 void InspectorCanvasAgent::requestContent(ErrorString& errorString, const String& canvasId, String* content)
167 {
168     auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
169     if (!inspectorCanvas)
170         return;
171
172     // FIXME: <https://webkit.org/b/180833> Web Inspector: support OffscreenCanvas for Canvas related operations
173
174     if (auto* node = inspectorCanvas->canvasElement()) {
175         if (is<CanvasRenderingContext2D>(inspectorCanvas->context()) || is<ImageBitmapRenderingContext>(inspectorCanvas->context())) {
176             auto result = node->toDataURL(ASCIILiteral("image/png"));
177             if (result.hasException()) {
178                 errorString = result.releaseException().releaseMessage();
179                 return;
180             }
181             *content = result.releaseReturnValue().string;
182             return;
183         }
184
185 #if ENABLE(WEBGL)
186         if (is<WebGLRenderingContextBase>(inspectorCanvas->context())) {
187             WebGLRenderingContextBase& contextWebGLBase = downcast<WebGLRenderingContextBase>(inspectorCanvas->context());
188
189             contextWebGLBase.setPreventBufferClearForInspector(true);
190             auto result = node->toDataURL(ASCIILiteral("image/png"));
191             contextWebGLBase.setPreventBufferClearForInspector(false);
192
193             if (result.hasException()) {
194                 errorString = result.releaseException().releaseMessage();
195                 return;
196             }
197             *content = result.releaseReturnValue().string;
198             return;
199         }
200 #endif
201     }
202
203     // FIXME: <https://webkit.org/b/173621> Web Inspector: Support getting the content of WebGPU context;
204     errorString = ASCIILiteral("Unsupported canvas context type");
205 }
206
207 void InspectorCanvasAgent::requestCSSCanvasClientNodes(ErrorString& errorString, const String& canvasId, RefPtr<JSON::ArrayOf<int>>& result)
208 {
209     auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
210     if (!inspectorCanvas)
211         return;
212
213     result = JSON::ArrayOf<int>::create();
214     for (auto* client : inspectorCanvas->context().canvasBase().cssCanvasClients()) {
215         if (int documentNodeId = m_instrumentingAgents.inspectorDOMAgent()->boundNodeId(&client->document()))
216             result->addItem(m_instrumentingAgents.inspectorDOMAgent()->pushNodeToFrontend(errorString, documentNodeId, client));
217     }
218 }
219
220 static JSC::JSValue contextAsScriptValue(JSC::ExecState& state, CanvasRenderingContext& context)
221 {
222     JSC::JSLockHolder lock(&state);
223
224     if (is<CanvasRenderingContext2D>(context))
225         return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<CanvasRenderingContext2D>(context));
226 #if ENABLE(WEBGL)
227     if (is<WebGLRenderingContext>(context))
228         return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<WebGLRenderingContext>(context));
229 #endif
230 #if ENABLE(WEBGL2)
231     if (is<WebGL2RenderingContext>(context))
232         return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<WebGL2RenderingContext>(context));
233 #endif
234 #if ENABLE(WEBGPU)
235     if (is<WebGPURenderingContext>(context))
236         return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<WebGPURenderingContext>(context));
237 #endif
238     if (is<ImageBitmapRenderingContext>(context))
239         return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<ImageBitmapRenderingContext>(context));
240
241     return { };
242 }
243
244 void InspectorCanvasAgent::resolveCanvasContext(ErrorString& errorString, const String& canvasId, const String* objectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result)
245 {
246     auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
247     if (!inspectorCanvas)
248         return;
249
250     auto& state = *inspectorCanvas->context().canvasBase().scriptExecutionContext()->execState();
251     auto injectedScript = m_injectedScriptManager.injectedScriptFor(&state);
252     ASSERT(!injectedScript.hasNoValue());
253
254     JSC::JSValue value = contextAsScriptValue(state, inspectorCanvas->context());
255     if (!value) {
256         ASSERT_NOT_REACHED();
257         errorString = ASCIILiteral("Unknown context type");
258         return;
259     }
260
261     String objectGroupName = objectGroup ? *objectGroup : String();
262     result = injectedScript.wrapObject(value, objectGroupName);
263 }
264
265 void InspectorCanvasAgent::startRecording(ErrorString& errorString, const String& canvasId, const bool* singleFrame, const int* memoryLimit)
266 {
267     auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
268     if (!inspectorCanvas)
269         return;
270
271     if (inspectorCanvas->context().callTracingActive()) {
272         errorString = ASCIILiteral("Already recording canvas");
273         return;
274     }
275
276     inspectorCanvas->resetRecordingData();
277     if (singleFrame)
278         inspectorCanvas->setSingleFrame(*singleFrame);
279     if (memoryLimit)
280         inspectorCanvas->setBufferLimit(*memoryLimit);
281
282     inspectorCanvas->context().setCallTracingActive(true);
283 }
284
285 void InspectorCanvasAgent::stopRecording(ErrorString& errorString, const String& canvasId)
286 {
287     auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
288     if (!inspectorCanvas)
289         return;
290
291     if (!inspectorCanvas->context().callTracingActive()) {
292         errorString = ASCIILiteral("No active recording for canvas");
293         return;
294     }
295
296     didFinishRecordingCanvasFrame(inspectorCanvas->context(), true);
297 }
298
299 void InspectorCanvasAgent::requestShaderSource(ErrorString& errorString, const String& programId, const String& shaderType, String* content)
300 {
301 #if ENABLE(WEBGL)
302     auto* inspectorProgram = assertInspectorProgram(errorString, programId);
303     if (!inspectorProgram)
304         return;
305
306     auto* shader = inspectorProgram->shaderForType(shaderType);
307     if (!shader) {
308         errorString = ASCIILiteral("No shader for given type.");
309         return;
310     }
311
312     *content = shader->getSource();
313 #else
314     UNUSED_PARAM(programId);
315     UNUSED_PARAM(shaderType);
316     UNUSED_PARAM(content);
317     errorString = ASCIILiteral("WebGL is not supported.");
318 #endif
319 }
320
321 void InspectorCanvasAgent::updateShader(ErrorString& errorString, const String& programId, const String& shaderType, const String& source)
322 {
323 #if ENABLE(WEBGL)
324     auto* inspectorProgram = assertInspectorProgram(errorString, programId);
325     if (!inspectorProgram)
326         return;
327
328     auto* shader = inspectorProgram->shaderForType(shaderType);
329     if (!shader) {
330         errorString = ASCIILiteral("No shader for given type.");
331         return;
332     }
333
334     WebGLRenderingContextBase& contextWebGL = inspectorProgram->context();
335     contextWebGL.shaderSource(shader, source);
336     contextWebGL.compileShader(shader);
337
338     if (!shader->isValid()) {
339         errorString = ASCIILiteral("Shader compilation failed.");
340         return;
341     }
342
343     contextWebGL.linkProgramWithoutInvalidatingAttribLocations(&inspectorProgram->program());
344 #else
345     UNUSED_PARAM(programId);
346     UNUSED_PARAM(shaderType);
347     UNUSED_PARAM(source);
348     errorString = ASCIILiteral("WebGL is not supported.");
349 #endif
350 }
351
352 void InspectorCanvasAgent::setShaderProgramDisabled(ErrorString& errorString, const String& programId, bool disabled)
353 {
354 #if ENABLE(WEBGL)
355     auto* inspectorProgram = assertInspectorProgram(errorString, programId);
356     if (!inspectorProgram)
357         return;
358
359     inspectorProgram->setDisabled(disabled);
360 #else
361     UNUSED_PARAM(programId);
362     UNUSED_PARAM(disabled);
363     errorString = ASCIILiteral("WebGL is not supported.");
364 #endif
365 }
366
367 void InspectorCanvasAgent::setShaderProgramHighlighted(ErrorString& errorString, const String& programId, bool highlighted)
368 {
369 #if ENABLE(WEBGL)
370     auto* inspectorProgram = assertInspectorProgram(errorString, programId);
371     if (!inspectorProgram)
372         return;
373
374     inspectorProgram->setHighlighted(highlighted);
375 #else
376     UNUSED_PARAM(programId);
377     UNUSED_PARAM(highlighted);
378     errorString = ASCIILiteral("WebGL is not supported.");
379 #endif
380 }
381
382 void InspectorCanvasAgent::frameNavigated(Frame& frame)
383 {
384     if (frame.isMainFrame()) {
385         clearCanvasData();
386         return;
387     }
388
389     Vector<InspectorCanvas*> inspectorCanvases;
390     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
391         if (auto* canvasElement = inspectorCanvas->canvasElement()) {
392             if (canvasElement->document().frame() == &frame)
393                 inspectorCanvases.append(inspectorCanvas.get());
394         }
395     }
396
397     for (auto* inspectorCanvas : inspectorCanvases) {
398         inspectorCanvas->context().canvasBase().removeObserver(*this);
399
400         String identifier = unbindCanvas(*inspectorCanvas);
401         if (m_enabled)
402             m_frontendDispatcher->canvasRemoved(identifier);
403     }
404 }
405
406 void InspectorCanvasAgent::didChangeCSSCanvasClientNodes(CanvasBase& canvasBase)
407 {
408     auto* context = canvasBase.renderingContext();
409     if (!context) {
410         ASSERT_NOT_REACHED();
411         return;
412     }
413
414     auto* inspectorCanvas = findInspectorCanvas(*context);
415     ASSERT(inspectorCanvas);
416     if (!inspectorCanvas)
417         return;
418
419     m_frontendDispatcher->cssCanvasClientNodesChanged(inspectorCanvas->identifier());
420 }
421
422 void InspectorCanvasAgent::didCreateCanvasRenderingContext(CanvasRenderingContext& context)
423 {
424     if (findInspectorCanvas(context)) {
425         ASSERT_NOT_REACHED();
426         return;
427     }
428
429     context.canvasBase().addObserver(*this);
430
431     auto inspectorCanvas = InspectorCanvas::create(context);
432
433     if (m_enabled) {
434         const bool captureBacktrace = true;
435         m_frontendDispatcher->canvasAdded(inspectorCanvas->buildObjectForCanvas(m_instrumentingAgents, captureBacktrace));
436     }
437
438     m_identifierToInspectorCanvas.set(inspectorCanvas->identifier(), WTFMove(inspectorCanvas));
439 }
440
441 void InspectorCanvasAgent::didChangeCanvasMemory(CanvasRenderingContext& context)
442 {
443     auto* inspectorCanvas = findInspectorCanvas(context);
444     ASSERT(inspectorCanvas);
445     if (!inspectorCanvas)
446         return;
447
448     // FIXME: <https://webkit.org/b/180833> Web Inspector: support OffscreenCanvas for Canvas related operations
449
450     if (auto* node = inspectorCanvas->canvasElement())
451         m_frontendDispatcher->canvasMemoryChanged(inspectorCanvas->identifier(), node->memoryCost());
452 }
453
454 void InspectorCanvasAgent::recordCanvasAction(CanvasRenderingContext& canvasRenderingContext, const String& name, Vector<RecordCanvasActionVariant>&& parameters)
455 {
456     auto* inspectorCanvas = findInspectorCanvas(canvasRenderingContext);
457     ASSERT(inspectorCanvas);
458     if (!inspectorCanvas)
459         return;
460
461     ASSERT(canvasRenderingContext.callTracingActive());
462     if (!canvasRenderingContext.callTracingActive())
463         return;
464
465     inspectorCanvas->recordAction(name, WTFMove(parameters));
466
467     if (!m_canvasRecordingTimer.isActive())
468         m_canvasRecordingTimer.startOneShot(0_s);
469
470     if (!inspectorCanvas->hasBufferSpace())
471         didFinishRecordingCanvasFrame(inspectorCanvas->context(), true);
472 }
473
474 void InspectorCanvasAgent::canvasDestroyed(CanvasBase& canvasBase)
475 {
476     auto* context = canvasBase.renderingContext();
477     if (!context)
478         return;
479
480     auto* inspectorCanvas = findInspectorCanvas(*context);
481     ASSERT(inspectorCanvas);
482     if (!inspectorCanvas)
483         return;
484
485     String identifier = unbindCanvas(*inspectorCanvas);
486     if (!m_enabled)
487         return;
488
489     // WebCore::CanvasObserver::canvasDestroyed is called in response to the GC destroying the CanvasBase.
490     // Due to the single-process model used in WebKit1, the event must be dispatched from a timer to prevent
491     // the frontend from making JS allocations while the GC is still active.
492     m_removedCanvasIdentifiers.append(identifier);
493
494     if (!m_canvasDestroyedTimer.isActive())
495         m_canvasDestroyedTimer.startOneShot(0_s);
496 }
497
498 void InspectorCanvasAgent::didFinishRecordingCanvasFrame(CanvasRenderingContext& context, bool forceDispatch)
499 {
500     auto* inspectorCanvas = findInspectorCanvas(context);
501     ASSERT(inspectorCanvas);
502     if (!inspectorCanvas)
503         return;
504
505     if (!inspectorCanvas->context().callTracingActive())
506         return;
507
508     if (!inspectorCanvas->hasRecordingData()) {
509         if (forceDispatch) {
510             m_frontendDispatcher->recordingFinished(inspectorCanvas->identifier(), nullptr);
511
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->singleFrame())
525         return;
526
527     // FIXME: <https://webkit.org/b/176008> Web Inspector: Record actions performed on WebGL2RenderingContext
528
529     Inspector::Protocol::Recording::Type type;
530     if (is<CanvasRenderingContext2D>(inspectorCanvas->context()))
531         type = Inspector::Protocol::Recording::Type::Canvas2D;
532 #if ENABLE(WEBGL)
533     else if (is<WebGLRenderingContext>(inspectorCanvas->context()))
534         type = Inspector::Protocol::Recording::Type::CanvasWebGL;
535 #endif
536     else {
537         ASSERT_NOT_REACHED();
538         type = Inspector::Protocol::Recording::Type::Canvas2D;
539     }
540
541     auto recording = Inspector::Protocol::Recording::Recording::create()
542         .setVersion(1)
543         .setType(type)
544         .setInitialState(inspectorCanvas->releaseInitialState())
545         .setData(inspectorCanvas->releaseData())
546         .release();
547
548     const String& name = inspectorCanvas->recordingName();
549     if (!name.isEmpty())
550         recording->setName(name);
551
552     m_frontendDispatcher->recordingFinished(inspectorCanvas->identifier(), WTFMove(recording));
553
554     inspectorCanvas->resetRecordingData();
555 }
556
557 void InspectorCanvasAgent::consoleStartRecordingCanvas(CanvasRenderingContext& context, JSC::ExecState& exec, JSC::JSObject* options)
558 {
559     auto* inspectorCanvas = findInspectorCanvas(context);
560     ASSERT(inspectorCanvas);
561     if (!inspectorCanvas)
562         return;
563
564     if (inspectorCanvas->context().callTracingActive())
565         return;
566
567     inspectorCanvas->resetRecordingData();
568
569     if (options) {
570         if (JSC::JSValue optionName = options->get(&exec, JSC::Identifier::fromString(&exec, "name")))
571             inspectorCanvas->setRecordingName(optionName.toWTFString(&exec));
572         if (JSC::JSValue optionSingleFrame = options->get(&exec, JSC::Identifier::fromString(&exec, "singleFrame")))
573             inspectorCanvas->setSingleFrame(optionSingleFrame.toBoolean(&exec));
574         if (JSC::JSValue optionMemoryLimit = options->get(&exec, JSC::Identifier::fromString(&exec, "memoryLimit")))
575             inspectorCanvas->setBufferLimit(optionMemoryLimit.toNumber(&exec));
576     }
577
578     inspectorCanvas->context().setCallTracingActive(true);
579 }
580
581 #if ENABLE(WEBGL)
582 void InspectorCanvasAgent::didEnableExtension(WebGLRenderingContextBase& context, const String& extension)
583 {
584     auto* inspectorCanvas = findInspectorCanvas(context);
585     ASSERT(inspectorCanvas);
586     if (!inspectorCanvas)
587         return;
588
589     m_frontendDispatcher->extensionEnabled(inspectorCanvas->identifier(), extension);
590 }
591
592 void InspectorCanvasAgent::didCreateProgram(WebGLRenderingContextBase& context, WebGLProgram& program)
593 {
594     auto* inspectorCanvas = findInspectorCanvas(context);
595     ASSERT(inspectorCanvas);
596     if (!inspectorCanvas)
597         return;
598
599     auto inspectorProgram = InspectorShaderProgram::create(program, *inspectorCanvas);
600     String programIdentifier = inspectorProgram->identifier();
601     m_identifierToInspectorProgram.set(programIdentifier, WTFMove(inspectorProgram));
602
603     if (m_enabled)
604         m_frontendDispatcher->programCreated(inspectorCanvas->identifier(), programIdentifier);
605 }
606
607 void InspectorCanvasAgent::willDeleteProgram(WebGLProgram& program)
608 {
609     auto* inspectorProgram = findInspectorProgram(program);
610     if (!inspectorProgram)
611         return;
612
613     String identifier = unbindProgram(*inspectorProgram);
614     if (m_enabled)
615         m_frontendDispatcher->programDeleted(identifier);
616 }
617
618 bool InspectorCanvasAgent::isShaderProgramDisabled(WebGLProgram& program)
619 {
620     auto* inspectorProgram = findInspectorProgram(program);
621     if (!inspectorProgram)
622         return false;
623
624     return inspectorProgram->disabled();
625 }
626
627 bool InspectorCanvasAgent::isShaderProgramHighlighted(WebGLProgram& program)
628 {
629     auto* inspectorProgram = findInspectorProgram(program);
630     if (!inspectorProgram)
631         return false;
632
633     return inspectorProgram->highlighted();
634 }
635 #endif
636
637 void InspectorCanvasAgent::canvasDestroyedTimerFired()
638 {
639     if (!m_removedCanvasIdentifiers.size())
640         return;
641
642     if (m_enabled) {
643         for (auto& identifier : m_removedCanvasIdentifiers)
644             m_frontendDispatcher->canvasRemoved(identifier);
645     }
646
647     m_removedCanvasIdentifiers.clear();
648 }
649
650 void InspectorCanvasAgent::canvasRecordingTimerFired()
651 {
652     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
653         if (!inspectorCanvas->context().callTracingActive())
654             continue;
655
656         didFinishRecordingCanvasFrame(inspectorCanvas->context());
657     }
658 }
659
660 void InspectorCanvasAgent::clearCanvasData()
661 {
662     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values())
663         inspectorCanvas->context().canvasBase().removeObserver(*this);
664
665     m_identifierToInspectorCanvas.clear();
666     m_removedCanvasIdentifiers.clear();
667 #if ENABLE(WEBGL)
668     m_identifierToInspectorProgram.clear();
669 #endif
670
671     if (m_canvasRecordingTimer.isActive())
672         m_canvasRecordingTimer.stop();
673
674     if (m_canvasDestroyedTimer.isActive())
675         m_canvasDestroyedTimer.stop();
676 }
677
678 String InspectorCanvasAgent::unbindCanvas(InspectorCanvas& inspectorCanvas)
679 {
680 #if ENABLE(WEBGL)
681     Vector<InspectorShaderProgram*> programsToRemove;
682     for (auto& inspectorProgram : m_identifierToInspectorProgram.values()) {
683         if (&inspectorProgram->canvas() == &inspectorCanvas)
684             programsToRemove.append(inspectorProgram.get());
685     }
686
687     for (auto* inspectorProgram : programsToRemove)
688         unbindProgram(*inspectorProgram);
689 #endif
690
691     String identifier = inspectorCanvas.identifier();
692     m_identifierToInspectorCanvas.remove(identifier);
693
694     return identifier;
695 }
696
697 InspectorCanvas* InspectorCanvasAgent::assertInspectorCanvas(ErrorString& errorString, const String& identifier)
698 {
699     RefPtr<InspectorCanvas> inspectorCanvas = m_identifierToInspectorCanvas.get(identifier);
700     if (!inspectorCanvas) {
701         errorString = ASCIILiteral("No canvas for given identifier.");
702         return nullptr;
703     }
704
705     return inspectorCanvas.get();
706 }
707
708 InspectorCanvas* InspectorCanvasAgent::findInspectorCanvas(CanvasRenderingContext& context)
709 {
710     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
711         if (&inspectorCanvas->context() == &context)
712             return inspectorCanvas.get();
713     }
714
715     return nullptr;
716 }
717
718 #if ENABLE(WEBGL)
719 String InspectorCanvasAgent::unbindProgram(InspectorShaderProgram& inspectorProgram)
720 {
721     String identifier = inspectorProgram.identifier();
722     m_identifierToInspectorProgram.remove(identifier);
723
724     return identifier;
725 }
726
727 InspectorShaderProgram* InspectorCanvasAgent::assertInspectorProgram(ErrorString& errorString, const String& identifier)
728 {
729     RefPtr<InspectorShaderProgram> inspectorProgram = m_identifierToInspectorProgram.get(identifier);
730     if (!inspectorProgram) {
731         errorString = ASCIILiteral("No shader program for given identifier.");
732         return nullptr;
733     }
734
735     return inspectorProgram.get();
736 }
737
738 InspectorShaderProgram* InspectorCanvasAgent::findInspectorProgram(WebGLProgram& program)
739 {
740     for (auto& inspectorProgram : m_identifierToInspectorProgram.values()) {
741         if (&inspectorProgram->program() == &program)
742             return inspectorProgram.get();
743     }
744
745     return nullptr;
746 }
747 #endif
748
749 } // namespace WebCore