Web Inspector: provide way for ShaderPrograms to be enabled/disabled
[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::setShaderProgramDisabled(ErrorString& errorString, const String& programId, bool disabled)
328 {
329 #if ENABLE(WEBGL)
330     auto* inspectorProgram = assertInspectorProgram(errorString, programId);
331     if (!inspectorProgram)
332         return;
333
334     inspectorProgram->setDisabled(disabled);
335 #else
336     UNUSED_PARAM(programId);
337     UNUSED_PARAM(disabled);
338     errorString = ASCIILiteral("WebGL is not supported.");
339 #endif
340 }
341
342 void InspectorCanvasAgent::frameNavigated(Frame& frame)
343 {
344     if (frame.isMainFrame()) {
345         clearCanvasData();
346         return;
347     }
348
349     Vector<InspectorCanvas*> inspectorCanvases;
350     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
351         if (inspectorCanvas->canvas().document().frame() == &frame)
352             inspectorCanvases.append(inspectorCanvas.get());
353     }
354
355     for (auto* inspectorCanvas : inspectorCanvases) {
356         String identifier = unbindCanvas(*inspectorCanvas);
357         if (m_enabled)
358             m_frontendDispatcher->canvasRemoved(identifier);
359     }
360 }
361
362 void InspectorCanvasAgent::didCreateCSSCanvas(HTMLCanvasElement& canvasElement, const String& name)
363 {
364     if (findInspectorCanvas(canvasElement)) {
365         ASSERT_NOT_REACHED();
366         return;
367     }
368
369     ASSERT(!m_canvasToCSSCanvasName.contains(&canvasElement));
370     m_canvasToCSSCanvasName.set(&canvasElement, name);
371 }
372
373 void InspectorCanvasAgent::didChangeCSSCanvasClientNodes(HTMLCanvasElement& canvasElement)
374 {
375     auto* inspectorCanvas = findInspectorCanvas(canvasElement);
376     if (!inspectorCanvas)
377         return;
378
379     m_frontendDispatcher->cssCanvasClientNodesChanged(inspectorCanvas->identifier());
380 }
381
382 void InspectorCanvasAgent::didCreateCanvasRenderingContext(HTMLCanvasElement& canvasElement)
383 {
384     if (findInspectorCanvas(canvasElement)) {
385         ASSERT_NOT_REACHED();
386         return;
387     }
388
389     canvasElement.addObserver(*this);
390
391     String cssCanvasName = m_canvasToCSSCanvasName.take(&canvasElement);
392     auto inspectorCanvas = InspectorCanvas::create(canvasElement, cssCanvasName);
393
394     if (m_enabled)
395         m_frontendDispatcher->canvasAdded(inspectorCanvas->buildObjectForCanvas(m_instrumentingAgents));
396
397     m_identifierToInspectorCanvas.set(inspectorCanvas->identifier(), WTFMove(inspectorCanvas));
398 }
399
400 void InspectorCanvasAgent::didChangeCanvasMemory(HTMLCanvasElement& canvasElement)
401 {
402     auto* inspectorCanvas = findInspectorCanvas(canvasElement);
403     if (!inspectorCanvas)
404         return;
405
406     m_frontendDispatcher->canvasMemoryChanged(inspectorCanvas->identifier(), canvasElement.memoryCost());
407 }
408
409 void InspectorCanvasAgent::recordCanvasAction(CanvasRenderingContext& canvasRenderingContext, const String& name, Vector<RecordCanvasActionVariant>&& parameters)
410 {
411     HTMLCanvasElement& canvasElement = canvasRenderingContext.canvas();
412
413     auto* inspectorCanvas = findInspectorCanvas(canvasElement);
414     ASSERT(inspectorCanvas);
415     if (!inspectorCanvas)
416         return;
417
418     ASSERT(canvasRenderingContext.callTracingActive());
419     if (!canvasRenderingContext.callTracingActive())
420         return;
421
422     inspectorCanvas->recordAction(name, WTFMove(parameters));
423
424     if (!m_canvasRecordingTimer.isActive())
425         m_canvasRecordingTimer.startOneShot(0_s);
426
427     if (!inspectorCanvas->hasBufferSpace())
428         didFinishRecordingCanvasFrame(canvasElement, true);
429 }
430
431 void InspectorCanvasAgent::canvasDestroyed(HTMLCanvasElement& canvasElement)
432 {
433     auto* inspectorCanvas = findInspectorCanvas(canvasElement);
434     ASSERT(inspectorCanvas);
435     if (!inspectorCanvas)
436         return;
437
438     String identifier = unbindCanvas(*inspectorCanvas);
439     if (!m_enabled)
440         return;
441
442     // WebCore::CanvasObserver::canvasDestroyed is called in response to the GC destroying the HTMLCanvasElement.
443     // Due to the single-process model used in WebKit1, the event must be dispatched from a timer to prevent
444     // the frontend from making JS allocations while the GC is still active.
445     m_removedCanvasIdentifiers.append(identifier);
446
447     if (!m_canvasDestroyedTimer.isActive())
448         m_canvasDestroyedTimer.startOneShot(0_s);
449 }
450
451 void InspectorCanvasAgent::didFinishRecordingCanvasFrame(HTMLCanvasElement& canvasElement, bool forceDispatch)
452 {
453     auto* inspectorCanvas = findInspectorCanvas(canvasElement);
454     ASSERT(inspectorCanvas);
455     if (!inspectorCanvas)
456         return;
457
458     CanvasRenderingContext* canvasRenderingContext = inspectorCanvas->canvas().renderingContext();
459     ASSERT(canvasRenderingContext->callTracingActive());
460     if (!canvasRenderingContext->callTracingActive())
461         return;
462
463     if (!inspectorCanvas->hasRecordingData())
464         return;
465
466     if (!forceDispatch && !inspectorCanvas->singleFrame()) {
467         inspectorCanvas->markNewFrame();
468         return;
469     }
470
471     if (forceDispatch)
472         inspectorCanvas->markCurrentFrameIncomplete();
473
474     // <https://webkit.org/b/174483> Web Inspector: Record actions performed on WebGLRenderingContext
475
476     Inspector::Protocol::Recording::Type type;
477     if (is<CanvasRenderingContext2D>(canvasRenderingContext))
478         type = Inspector::Protocol::Recording::Type::Canvas2D;
479     else {
480         ASSERT_NOT_REACHED();
481         type = Inspector::Protocol::Recording::Type::Canvas2D;
482     }
483
484     auto recording = Inspector::Protocol::Recording::Recording::create()
485         .setVersion(1)
486         .setType(type)
487         .setInitialState(inspectorCanvas->releaseInitialState())
488         .setFrames(inspectorCanvas->releaseFrames())
489         .setData(inspectorCanvas->releaseData())
490         .release();
491
492     m_frontendDispatcher->recordingFinished(inspectorCanvas->identifier(), WTFMove(recording));
493
494     inspectorCanvas->resetRecordingData();
495 }
496
497 #if ENABLE(WEBGL)
498 void InspectorCanvasAgent::didCreateProgram(WebGLRenderingContextBase& context, WebGLProgram& program)
499 {
500     auto* inspectorCanvas = findInspectorCanvas(context.canvas());
501     ASSERT(inspectorCanvas);
502     if (!inspectorCanvas)
503         return;
504
505     auto inspectorProgram = InspectorShaderProgram::create(program, *inspectorCanvas);
506     String programIdentifier = inspectorProgram->identifier();
507     m_identifierToInspectorProgram.set(programIdentifier, WTFMove(inspectorProgram));
508
509     if (m_enabled)
510         m_frontendDispatcher->programCreated(inspectorCanvas->identifier(), programIdentifier);
511 }
512
513 void InspectorCanvasAgent::willDeleteProgram(WebGLProgram& program)
514 {
515     auto* inspectorProgram = findInspectorProgram(program);
516     if (!inspectorProgram)
517         return;
518
519     String identifier = unbindProgram(*inspectorProgram);
520     if (m_enabled)
521         m_frontendDispatcher->programDeleted(identifier);
522 }
523
524 bool InspectorCanvasAgent::isShaderProgramDisabled(WebGLProgram& program)
525 {
526     auto* inspectorProgram = findInspectorProgram(program);
527     if (!inspectorProgram)
528         return false;
529
530     return inspectorProgram->disabled();
531 }
532 #endif
533
534 void InspectorCanvasAgent::canvasDestroyedTimerFired()
535 {
536     if (!m_removedCanvasIdentifiers.size())
537         return;
538
539     if (m_enabled) {
540         for (auto& identifier : m_removedCanvasIdentifiers)
541             m_frontendDispatcher->canvasRemoved(identifier);
542     }
543
544     m_removedCanvasIdentifiers.clear();
545 }
546
547 void InspectorCanvasAgent::canvasRecordingTimerFired()
548 {
549     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
550         if (!inspectorCanvas->canvas().renderingContext()->callTracingActive())
551             continue;
552
553         didFinishRecordingCanvasFrame(inspectorCanvas->canvas());
554     }
555 }
556
557 void InspectorCanvasAgent::clearCanvasData()
558 {
559     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values())
560         inspectorCanvas->canvas().removeObserver(*this);
561
562     m_identifierToInspectorCanvas.clear();
563     m_canvasToCSSCanvasName.clear();
564     m_removedCanvasIdentifiers.clear();
565 #if ENABLE(WEBGL)
566     m_identifierToInspectorProgram.clear();
567 #endif
568
569     if (m_canvasRecordingTimer.isActive())
570         m_canvasRecordingTimer.stop();
571
572     if (m_canvasDestroyedTimer.isActive())
573         m_canvasDestroyedTimer.stop();
574 }
575
576 String InspectorCanvasAgent::unbindCanvas(InspectorCanvas& inspectorCanvas)
577 {
578     ASSERT(!m_canvasToCSSCanvasName.contains(&inspectorCanvas.canvas()));
579
580 #if ENABLE(WEBGL)
581     Vector<InspectorShaderProgram*> programsToRemove;
582     for (auto& inspectorProgram : m_identifierToInspectorProgram.values()) {
583         if (&inspectorProgram->canvas() == &inspectorCanvas)
584             programsToRemove.append(inspectorProgram.get());
585     }
586
587     for (auto* inspectorProgram : programsToRemove)
588         unbindProgram(*inspectorProgram);
589 #endif
590
591     String identifier = inspectorCanvas.identifier();
592     m_identifierToInspectorCanvas.remove(identifier);
593
594     return identifier;
595 }
596
597 InspectorCanvas* InspectorCanvasAgent::assertInspectorCanvas(ErrorString& errorString, const String& identifier)
598 {
599     RefPtr<InspectorCanvas> inspectorCanvas = m_identifierToInspectorCanvas.get(identifier);
600     if (!inspectorCanvas) {
601         errorString = ASCIILiteral("No canvas for given identifier.");
602         return nullptr;
603     }
604
605     return inspectorCanvas.get();
606 }
607
608 InspectorCanvas* InspectorCanvasAgent::findInspectorCanvas(HTMLCanvasElement& canvasElement)
609 {
610     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
611         if (&inspectorCanvas->canvas() == &canvasElement)
612             return inspectorCanvas.get();
613     }
614
615     return nullptr;
616 }
617
618 #if ENABLE(WEBGL)
619 String InspectorCanvasAgent::unbindProgram(InspectorShaderProgram& inspectorProgram)
620 {
621     ASSERT(inspectorProgram.context());
622
623     String identifier = inspectorProgram.identifier();
624     m_identifierToInspectorProgram.remove(identifier);
625
626     return identifier;
627 }
628
629 InspectorShaderProgram* InspectorCanvasAgent::assertInspectorProgram(ErrorString& errorString, const String& identifier)
630 {
631     RefPtr<InspectorShaderProgram> inspectorProgram = m_identifierToInspectorProgram.get(identifier);
632     if (!inspectorProgram) {
633         errorString = ASCIILiteral("No shader program for given identifier.");
634         return nullptr;
635     }
636
637     return inspectorProgram.get();
638 }
639
640 InspectorShaderProgram* InspectorCanvasAgent::findInspectorProgram(WebGLProgram& program)
641 {
642     for (auto& inspectorProgram : m_identifierToInspectorProgram.values()) {
643         if (&inspectorProgram->program() == &program)
644             return inspectorProgram.get();
645     }
646
647     return nullptr;
648 }
649 #endif
650
651 } // namespace WebCore