2b0ee71fa80ab1c2ce4588b6820b905cde96adfa
[WebKit-https.git] / Source / WebCore / inspector / 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 "InspectorDOMAgent.h"
35 #include "InstrumentingAgents.h"
36 #include "JSCanvasRenderingContext2D.h"
37 #include "JSMainThreadExecState.h"
38 #include "MainFrame.h"
39 #include "ScriptState.h"
40 #include "StringAdaptors.h"
41 #include <inspector/IdentifiersFactory.h>
42 #include <inspector/InjectedScript.h>
43 #include <inspector/InjectedScriptManager.h>
44 #include <inspector/InspectorProtocolObjects.h>
45 #include <runtime/JSCInlines.h>
46
47 #if ENABLE(WEBGL)
48 #include "JSWebGLRenderingContext.h"
49 #include "WebGLProgram.h"
50 #include "WebGLShader.h"
51 #endif
52
53 #if ENABLE(WEBGL2)
54 #include "JSWebGL2RenderingContext.h"
55 #endif
56
57 #if ENABLE(WEBGPU)
58 #include "JSWebGPURenderingContext.h"
59 #endif
60
61 using namespace Inspector;
62
63 namespace WebCore {
64
65 InspectorCanvasAgent::InspectorCanvasAgent(WebAgentContext& context)
66     : InspectorAgentBase(ASCIILiteral("Canvas"), context)
67     , m_frontendDispatcher(std::make_unique<Inspector::CanvasFrontendDispatcher>(context.frontendRouter))
68     , m_backendDispatcher(Inspector::CanvasBackendDispatcher::create(context.backendDispatcher, this))
69     , m_injectedScriptManager(context.injectedScriptManager)
70     , m_canvasDestroyedTimer(*this, &InspectorCanvasAgent::canvasDestroyedTimerFired)
71     , m_canvasRecordingTimer(*this, &InspectorCanvasAgent::canvasRecordingTimerFired)
72 {
73 }
74
75 void InspectorCanvasAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
76 {
77 }
78
79 void InspectorCanvasAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
80 {
81     ErrorString ignored;
82     disable(ignored);
83 }
84
85 void InspectorCanvasAgent::discardAgent()
86 {
87     clearCanvasData();
88 }
89
90 void InspectorCanvasAgent::enable(ErrorString&)
91 {
92     if (m_enabled)
93         return;
94
95     m_enabled = true;
96
97     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values())
98         m_frontendDispatcher->canvasAdded(inspectorCanvas->buildObjectForCanvas(m_instrumentingAgents));
99
100 #if ENABLE(WEBGL)
101     for (auto& inspectorProgram : m_identifierToInspectorProgram.values()) {
102         auto& inspectorCanvas = inspectorProgram->canvas();
103         m_frontendDispatcher->programCreated(inspectorCanvas.identifier(), inspectorProgram->identifier());
104     }
105 #endif
106 }
107
108 void InspectorCanvasAgent::disable(ErrorString&)
109 {
110     if (!m_enabled)
111         return;
112
113     if (m_canvasDestroyedTimer.isActive())
114         m_canvasDestroyedTimer.stop();
115
116     m_removedCanvasIdentifiers.clear();
117
118     if (m_canvasRecordingTimer.isActive())
119         m_canvasRecordingTimer.stop();
120
121     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values())
122         inspectorCanvas->resetRecordingData();
123
124     m_enabled = false;
125 }
126
127 void InspectorCanvasAgent::requestNode(ErrorString& errorString, const String& canvasId, int* nodeId)
128 {
129     auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
130     if (!inspectorCanvas)
131         return;
132
133     int documentNodeId = m_instrumentingAgents.inspectorDOMAgent()->boundNodeId(&inspectorCanvas->canvas().document());
134     if (!documentNodeId) {
135         errorString = ASCIILiteral("Document has not been requested");
136         return;
137     }
138
139     *nodeId = m_instrumentingAgents.inspectorDOMAgent()->pushNodeToFrontend(errorString, documentNodeId, &inspectorCanvas->canvas());
140 }
141
142 void InspectorCanvasAgent::requestContent(ErrorString& errorString, const String& canvasId, String* content)
143 {
144     auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
145     if (!inspectorCanvas)
146         return;
147
148     CanvasRenderingContext* context = inspectorCanvas->canvas().renderingContext();
149     if (is<CanvasRenderingContext2D>(context)) {
150         auto result = inspectorCanvas->canvas().toDataURL(ASCIILiteral("image/png"));
151         if (result.hasException()) {
152             errorString = result.releaseException().releaseMessage();
153             return;
154         }
155         *content = result.releaseReturnValue().string;
156     }
157 #if ENABLE(WEBGL)
158     else if (is<WebGLRenderingContextBase>(context)) {
159         WebGLRenderingContextBase* gl = downcast<WebGLRenderingContextBase>(context);
160
161         gl->setPreventBufferClearForInspector(true);
162         auto result = inspectorCanvas->canvas().toDataURL(ASCIILiteral("image/png"));
163         gl->setPreventBufferClearForInspector(false);
164
165         if (result.hasException()) {
166             errorString = result.releaseException().releaseMessage();
167             return;
168         }
169         *content = result.releaseReturnValue().string;
170     }
171 #endif
172     // FIXME: <https://webkit.org/b/173621> Web Inspector: Support getting the content of WebGPU contexts
173     else
174         errorString = ASCIILiteral("Unsupported canvas context type");
175 }
176
177 void InspectorCanvasAgent::requestCSSCanvasClientNodes(ErrorString& errorString, const String& canvasId, RefPtr<Inspector::Protocol::Array<int>>& result)
178 {
179     auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
180     if (!inspectorCanvas)
181         return;
182
183     result = Inspector::Protocol::Array<int>::create();
184     for (Element* element : inspectorCanvas->canvas().cssCanvasClients()) {
185         if (int documentNodeId = m_instrumentingAgents.inspectorDOMAgent()->boundNodeId(&element->document()))
186             result->addItem(m_instrumentingAgents.inspectorDOMAgent()->pushNodeToFrontend(errorString, documentNodeId, element));
187     }
188 }
189
190 static JSC::JSValue contextAsScriptValue(JSC::ExecState& state, CanvasRenderingContext* context)
191 {
192     JSC::JSLockHolder lock(&state);
193
194     if (is<CanvasRenderingContext2D>(context))
195         return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<CanvasRenderingContext2D>(context));
196 #if ENABLE(WEBGL)
197     if (is<WebGLRenderingContext>(context))
198         return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<WebGLRenderingContext>(context));
199 #endif
200 #if ENABLE(WEBGL2)
201     if (is<WebGL2RenderingContext>(context))
202         return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<WebGL2RenderingContext>(context));
203 #endif
204 #if ENABLE(WEBGPU)
205     if (is<WebGPURenderingContext>(context))
206         return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<WebGPURenderingContext>(context));
207 #endif
208
209     return { };
210 }
211
212 void InspectorCanvasAgent::resolveCanvasContext(ErrorString& errorString, const String& canvasId, const String* const objectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result)
213 {
214     auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
215     if (!inspectorCanvas)
216         return;
217
218     Frame* frame = inspectorCanvas->canvas().document().frame();
219     if (!frame) {
220         errorString = ASCIILiteral("Canvas belongs to a document without a frame");
221         return;
222     }
223
224     auto& state = *mainWorldExecState(frame);
225     auto injectedScript = m_injectedScriptManager.injectedScriptFor(&state);
226     ASSERT(!injectedScript.hasNoValue());
227
228     CanvasRenderingContext* context = inspectorCanvas->canvas().renderingContext();
229     JSC::JSValue value = contextAsScriptValue(state, context);
230     if (!value) {
231         ASSERT_NOT_REACHED();
232         errorString = ASCIILiteral("Unknown context type");
233         return;
234     }
235
236     String objectGroupName = objectGroup ? *objectGroup : String();
237     result = injectedScript.wrapObject(value, objectGroupName);
238 }
239
240 void InspectorCanvasAgent::requestRecording(ErrorString& errorString, const String& canvasId, const bool* const singleFrame, const int* const memoryLimit)
241 {
242     auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
243     if (!inspectorCanvas)
244         return;
245
246     if (inspectorCanvas->canvas().renderingContext()->callTracingActive()) {
247         errorString = ASCIILiteral("Already recording canvas");
248         return;
249     }
250
251     inspectorCanvas->resetRecordingData();
252     if (singleFrame)
253         inspectorCanvas->setSingleFrame(*singleFrame);
254     if (memoryLimit)
255         inspectorCanvas->setBufferLimit(*memoryLimit);
256
257     inspectorCanvas->canvas().renderingContext()->setCallTracingActive(true);
258 }
259
260 void InspectorCanvasAgent::cancelRecording(ErrorString& errorString, const String& canvasId)
261 {
262     auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
263     if (!inspectorCanvas)
264         return;
265
266     if (!inspectorCanvas->canvas().renderingContext()->callTracingActive()) {
267         errorString = ASCIILiteral("No active recording for canvas");
268         return;
269     }
270
271     didFinishRecordingCanvasFrame(inspectorCanvas->canvas(), true);
272 }
273
274 void InspectorCanvasAgent::requestShaderSource(ErrorString& errorString, const String& programId, const String& shaderType, String* content)
275 {
276 #if ENABLE(WEBGL)
277     auto* inspectorProgram = assertInspectorProgram(errorString, programId);
278     if (!inspectorProgram)
279         return;
280
281     auto* shader = inspectorProgram->shaderForType(shaderType);
282     if (!shader) {
283         errorString = ASCIILiteral("No shader for given type.");
284         return;
285     }
286
287     *content = shader->getSource();
288 #else
289     UNUSED_PARAM(programId);
290     UNUSED_PARAM(shaderType);
291     UNUSED_PARAM(content);
292     errorString = ASCIILiteral("WebGL is not supported.");
293 #endif
294 }
295
296 void InspectorCanvasAgent::updateShader(ErrorString& errorString, const String& programId, const String& shaderType, const String& source)
297 {
298 #if ENABLE(WEBGL)
299     auto* inspectorProgram = assertInspectorProgram(errorString, programId);
300     if (!inspectorProgram)
301         return;
302
303     auto* shader = inspectorProgram->shaderForType(shaderType);
304     if (!shader) {
305         errorString = ASCIILiteral("No shader for given type.");
306         return;
307     }
308
309     WebGLRenderingContextBase* contextWebGL = inspectorProgram->context();
310     contextWebGL->shaderSource(shader, source);
311     contextWebGL->compileShader(shader);
312
313     if (!shader->isValid()) {
314         errorString = ASCIILiteral("Shader compilation failed.");
315         return;
316     }
317
318     contextWebGL->linkProgramWithoutInvalidatingAttribLocations(&inspectorProgram->program());
319 #else
320     UNUSED_PARAM(programId);
321     UNUSED_PARAM(shaderType);
322     UNUSED_PARAM(source);
323     errorString = ASCIILiteral("WebGL is not supported.");
324 #endif
325 }
326
327 void InspectorCanvasAgent::frameNavigated(Frame& frame)
328 {
329     if (frame.isMainFrame()) {
330         clearCanvasData();
331         return;
332     }
333
334     Vector<InspectorCanvas*> inspectorCanvases;
335     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
336         if (inspectorCanvas->canvas().document().frame() == &frame)
337             inspectorCanvases.append(inspectorCanvas.get());
338     }
339
340     for (auto* inspectorCanvas : inspectorCanvases) {
341         String identifier = unbindCanvas(*inspectorCanvas);
342         if (m_enabled)
343             m_frontendDispatcher->canvasRemoved(identifier);
344     }
345 }
346
347 void InspectorCanvasAgent::didCreateCSSCanvas(HTMLCanvasElement& canvasElement, const String& name)
348 {
349     if (findInspectorCanvas(canvasElement)) {
350         ASSERT_NOT_REACHED();
351         return;
352     }
353
354     ASSERT(!m_canvasToCSSCanvasName.contains(&canvasElement));
355     m_canvasToCSSCanvasName.set(&canvasElement, name);
356 }
357
358 void InspectorCanvasAgent::didChangeCSSCanvasClientNodes(HTMLCanvasElement& canvasElement)
359 {
360     auto* inspectorCanvas = findInspectorCanvas(canvasElement);
361     if (!inspectorCanvas)
362         return;
363
364     m_frontendDispatcher->cssCanvasClientNodesChanged(inspectorCanvas->identifier());
365 }
366
367 void InspectorCanvasAgent::didCreateCanvasRenderingContext(HTMLCanvasElement& canvasElement)
368 {
369     if (findInspectorCanvas(canvasElement)) {
370         ASSERT_NOT_REACHED();
371         return;
372     }
373
374     canvasElement.addObserver(*this);
375
376     String cssCanvasName = m_canvasToCSSCanvasName.take(&canvasElement);
377     auto inspectorCanvas = InspectorCanvas::create(canvasElement, cssCanvasName);
378
379     if (m_enabled)
380         m_frontendDispatcher->canvasAdded(inspectorCanvas->buildObjectForCanvas(m_instrumentingAgents));
381
382     m_identifierToInspectorCanvas.set(inspectorCanvas->identifier(), WTFMove(inspectorCanvas));
383 }
384
385 void InspectorCanvasAgent::didChangeCanvasMemory(HTMLCanvasElement& canvasElement)
386 {
387     auto* inspectorCanvas = findInspectorCanvas(canvasElement);
388     if (!inspectorCanvas)
389         return;
390
391     m_frontendDispatcher->canvasMemoryChanged(inspectorCanvas->identifier(), canvasElement.memoryCost());
392 }
393
394 void InspectorCanvasAgent::recordCanvasAction(CanvasRenderingContext& canvasRenderingContext, const String& name, Vector<RecordCanvasActionVariant>&& parameters)
395 {
396     HTMLCanvasElement& canvasElement = canvasRenderingContext.canvas();
397
398     auto* inspectorCanvas = findInspectorCanvas(canvasElement);
399     ASSERT(inspectorCanvas);
400     if (!inspectorCanvas)
401         return;
402
403     ASSERT(canvasRenderingContext.callTracingActive());
404     if (!canvasRenderingContext.callTracingActive())
405         return;
406
407     inspectorCanvas->recordAction(name, WTFMove(parameters));
408
409     if (!m_canvasRecordingTimer.isActive())
410         m_canvasRecordingTimer.startOneShot(0_s);
411
412     if (!inspectorCanvas->hasBufferSpace())
413         didFinishRecordingCanvasFrame(canvasElement, true);
414 }
415
416 void InspectorCanvasAgent::canvasDestroyed(HTMLCanvasElement& canvasElement)
417 {
418     auto* inspectorCanvas = findInspectorCanvas(canvasElement);
419     ASSERT(inspectorCanvas);
420     if (!inspectorCanvas)
421         return;
422
423     String identifier = unbindCanvas(*inspectorCanvas);
424     if (!m_enabled)
425         return;
426
427     // WebCore::CanvasObserver::canvasDestroyed is called in response to the GC destroying the HTMLCanvasElement.
428     // Due to the single-process model used in WebKit1, the event must be dispatched from a timer to prevent
429     // the frontend from making JS allocations while the GC is still active.
430     m_removedCanvasIdentifiers.append(identifier);
431
432     if (!m_canvasDestroyedTimer.isActive())
433         m_canvasDestroyedTimer.startOneShot(0_s);
434 }
435
436 void InspectorCanvasAgent::didFinishRecordingCanvasFrame(HTMLCanvasElement& canvasElement, bool forceDispatch)
437 {
438     auto* inspectorCanvas = findInspectorCanvas(canvasElement);
439     ASSERT(inspectorCanvas);
440     if (!inspectorCanvas)
441         return;
442
443     CanvasRenderingContext* canvasRenderingContext = inspectorCanvas->canvas().renderingContext();
444     ASSERT(canvasRenderingContext->callTracingActive());
445     if (!canvasRenderingContext->callTracingActive())
446         return;
447
448     if (!inspectorCanvas->hasRecordingData())
449         return;
450
451     if (!forceDispatch && !inspectorCanvas->singleFrame()) {
452         inspectorCanvas->markNewFrame();
453         return;
454     }
455
456     if (forceDispatch)
457         inspectorCanvas->markCurrentFrameIncomplete();
458
459     // <https://webkit.org/b/174483> Web Inspector: Record actions performed on WebGLRenderingContext
460
461     Inspector::Protocol::Recording::Type type;
462     if (is<CanvasRenderingContext2D>(canvasRenderingContext))
463         type = Inspector::Protocol::Recording::Type::Canvas2D;
464     else {
465         ASSERT_NOT_REACHED();
466         type = Inspector::Protocol::Recording::Type::Canvas2D;
467     }
468
469     auto recording = Inspector::Protocol::Recording::Recording::create()
470         .setVersion(1)
471         .setType(type)
472         .setInitialState(inspectorCanvas->releaseInitialState())
473         .setFrames(inspectorCanvas->releaseFrames())
474         .setData(inspectorCanvas->releaseData())
475         .release();
476
477     m_frontendDispatcher->recordingFinished(inspectorCanvas->identifier(), WTFMove(recording));
478
479     inspectorCanvas->resetRecordingData();
480 }
481
482 #if ENABLE(WEBGL)
483 void InspectorCanvasAgent::didCreateProgram(WebGLRenderingContextBase& context, WebGLProgram& program)
484 {
485     auto* inspectorCanvas = findInspectorCanvas(context.canvas());
486     ASSERT(inspectorCanvas);
487     if (!inspectorCanvas)
488         return;
489
490     auto inspectorProgram = InspectorShaderProgram::create(program, *inspectorCanvas);
491     String programIdentifier = inspectorProgram->identifier();
492     m_identifierToInspectorProgram.set(programIdentifier, WTFMove(inspectorProgram));
493
494     if (m_enabled)
495         m_frontendDispatcher->programCreated(inspectorCanvas->identifier(), programIdentifier);
496 }
497
498 void InspectorCanvasAgent::willDeleteProgram(WebGLProgram& program)
499 {
500     auto* inspectorProgram = findInspectorProgram(program);
501     if (!inspectorProgram)
502         return;
503
504     String identifier = unbindProgram(*inspectorProgram);
505     if (m_enabled)
506         m_frontendDispatcher->programDeleted(identifier);
507 }
508 #endif
509
510 void InspectorCanvasAgent::canvasDestroyedTimerFired()
511 {
512     if (!m_removedCanvasIdentifiers.size())
513         return;
514
515     if (m_enabled) {
516         for (auto& identifier : m_removedCanvasIdentifiers)
517             m_frontendDispatcher->canvasRemoved(identifier);
518     }
519
520     m_removedCanvasIdentifiers.clear();
521 }
522
523 void InspectorCanvasAgent::canvasRecordingTimerFired()
524 {
525     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
526         if (!inspectorCanvas->canvas().renderingContext()->callTracingActive())
527             continue;
528
529         didFinishRecordingCanvasFrame(inspectorCanvas->canvas());
530     }
531 }
532
533 void InspectorCanvasAgent::clearCanvasData()
534 {
535     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values())
536         inspectorCanvas->canvas().removeObserver(*this);
537
538     m_identifierToInspectorCanvas.clear();
539     m_canvasToCSSCanvasName.clear();
540     m_removedCanvasIdentifiers.clear();
541 #if ENABLE(WEBGL)
542     m_identifierToInspectorProgram.clear();
543 #endif
544
545     if (m_canvasRecordingTimer.isActive())
546         m_canvasRecordingTimer.stop();
547
548     if (m_canvasDestroyedTimer.isActive())
549         m_canvasDestroyedTimer.stop();
550 }
551
552 String InspectorCanvasAgent::unbindCanvas(InspectorCanvas& inspectorCanvas)
553 {
554     ASSERT(!m_canvasToCSSCanvasName.contains(&inspectorCanvas.canvas()));
555
556 #if ENABLE(WEBGL)
557     Vector<InspectorShaderProgram*> programsToRemove;
558     for (auto& inspectorProgram : m_identifierToInspectorProgram.values()) {
559         if (&inspectorProgram->canvas() == &inspectorCanvas)
560             programsToRemove.append(inspectorProgram.get());
561     }
562
563     for (auto* inspectorProgram : programsToRemove)
564         unbindProgram(*inspectorProgram);
565 #endif
566
567     String identifier = inspectorCanvas.identifier();
568     m_identifierToInspectorCanvas.remove(identifier);
569
570     return identifier;
571 }
572
573 InspectorCanvas* InspectorCanvasAgent::assertInspectorCanvas(ErrorString& errorString, const String& identifier)
574 {
575     RefPtr<InspectorCanvas> inspectorCanvas = m_identifierToInspectorCanvas.get(identifier);
576     if (!inspectorCanvas) {
577         errorString = ASCIILiteral("No canvas for given identifier.");
578         return nullptr;
579     }
580
581     return inspectorCanvas.get();
582 }
583
584 InspectorCanvas* InspectorCanvasAgent::findInspectorCanvas(HTMLCanvasElement& canvasElement)
585 {
586     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
587         if (&inspectorCanvas->canvas() == &canvasElement)
588             return inspectorCanvas.get();
589     }
590
591     return nullptr;
592 }
593
594 #if ENABLE(WEBGL)
595 String InspectorCanvasAgent::unbindProgram(InspectorShaderProgram& inspectorProgram)
596 {
597     ASSERT(inspectorProgram.context());
598
599     String identifier = inspectorProgram.identifier();
600     m_identifierToInspectorProgram.remove(identifier);
601
602     return identifier;
603 }
604
605 InspectorShaderProgram* InspectorCanvasAgent::assertInspectorProgram(ErrorString& errorString, const String& identifier)
606 {
607     RefPtr<InspectorShaderProgram> inspectorProgram = m_identifierToInspectorProgram.get(identifier);
608     if (!inspectorProgram) {
609         errorString = ASCIILiteral("No shader program for given identifier.");
610         return nullptr;
611     }
612
613     return inspectorProgram.get();
614 }
615
616 InspectorShaderProgram* InspectorCanvasAgent::findInspectorProgram(WebGLProgram& program)
617 {
618     for (auto& inspectorProgram : m_identifierToInspectorProgram.values()) {
619         if (&inspectorProgram->program() == &program)
620             return inspectorProgram.get();
621     }
622
623     return nullptr;
624 }
625 #endif
626
627 } // namespace WebCore