[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[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 "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 "InstrumentingAgents.h"
39 #include "JSCanvasRenderingContext2D.h"
40 #include "JSExecState.h"
41 #include "JSImageBitmapRenderingContext.h"
42 #include "Microtasks.h"
43 #include "OffscreenCanvas.h"
44 #include "ScriptState.h"
45 #include "StringAdaptors.h"
46 #include <JavaScriptCore/IdentifiersFactory.h>
47 #include <JavaScriptCore/InjectedScript.h>
48 #include <JavaScriptCore/InjectedScriptManager.h>
49 #include <JavaScriptCore/InspectorProtocolObjects.h>
50 #include <JavaScriptCore/JSCInlines.h>
51 #include <wtf/HashSet.h>
52 #include <wtf/Lock.h>
53
54 #if ENABLE(WEBGL)
55 #include "JSWebGLRenderingContext.h"
56 #include "WebGLProgram.h"
57 #include "WebGLShader.h"
58 #endif
59
60 #if ENABLE(WEBGL2)
61 #include "JSWebGL2RenderingContext.h"
62 #endif
63
64 #if ENABLE(WEBGPU)
65 #include "JSGPUCanvasContext.h"
66 #endif
67
68 namespace WebCore {
69
70 using namespace Inspector;
71
72 InspectorCanvasAgent::InspectorCanvasAgent(PageAgentContext& context)
73     : InspectorAgentBase("Canvas"_s, context)
74     , m_frontendDispatcher(makeUnique<Inspector::CanvasFrontendDispatcher>(context.frontendRouter))
75     , m_backendDispatcher(Inspector::CanvasBackendDispatcher::create(context.backendDispatcher, this))
76     , m_injectedScriptManager(context.injectedScriptManager)
77     , m_inspectedPage(context.inspectedPage)
78     , m_canvasDestroyedTimer(*this, &InspectorCanvasAgent::canvasDestroyedTimerFired)
79 {
80 }
81
82 void InspectorCanvasAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
83 {
84 }
85
86 void InspectorCanvasAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
87 {
88     ErrorString ignored;
89     disable(ignored);
90 }
91
92 void InspectorCanvasAgent::discardAgent()
93 {
94     clearCanvasData();
95 }
96
97 void InspectorCanvasAgent::enable(ErrorString&)
98 {
99     if (m_instrumentingAgents.inspectorCanvasAgent() == this)
100         return;
101
102     m_instrumentingAgents.setInspectorCanvasAgent(this);
103
104     const auto canvasExistsInCurrentPage = [&] (CanvasRenderingContext* canvasRenderingContext) {
105         if (!canvasRenderingContext)
106             return false;
107
108         auto* scriptExecutionContext = canvasRenderingContext->canvasBase().scriptExecutionContext();
109         if (!is<Document>(scriptExecutionContext))
110             return false;
111
112         // FIXME: <https://webkit.org/b/168475> Web Inspector: Correctly display iframe's WebSockets
113         auto* document = downcast<Document>(scriptExecutionContext);
114         return document->page() == &m_inspectedPage;
115     };
116
117     {
118         LockHolder lock(CanvasRenderingContext::instancesMutex());
119         for (auto* canvasRenderingContext : CanvasRenderingContext::instances(lock)) {
120             if (canvasExistsInCurrentPage(canvasRenderingContext))
121                 bindCanvas(*canvasRenderingContext, false);
122         }
123     }
124
125 #if ENABLE(WEBGL)
126     {
127         LockHolder lock(WebGLProgram::instancesMutex());
128         for (auto& entry : WebGLProgram::instances(lock)) {
129             if (canvasExistsInCurrentPage(entry.value))
130                 didCreateProgram(*entry.value, *entry.key);
131         }
132     }
133 #endif
134 }
135
136 void InspectorCanvasAgent::disable(ErrorString&)
137 {
138     m_instrumentingAgents.setInspectorCanvasAgent(nullptr);
139
140     clearCanvasData();
141
142     m_recordingAutoCaptureFrameCount = WTF::nullopt;
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 = "No node for canvas"_s;
154         return;
155     }
156
157     int documentNodeId = m_instrumentingAgents.inspectorDOMAgent()->boundNodeId(&node->document());
158     if (!documentNodeId) {
159         errorString = "Document has not been requested"_s;
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     *content = inspectorCanvas->getCanvasContentAsDataURL(errorString);
173 }
174
175 void InspectorCanvasAgent::requestCSSCanvasClientNodes(ErrorString& errorString, const String& canvasId, RefPtr<JSON::ArrayOf<int>>& result)
176 {
177     auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
178     if (!inspectorCanvas)
179         return;
180
181     result = JSON::ArrayOf<int>::create();
182     for (auto* client : inspectorCanvas->context().canvasBase().cssCanvasClients()) {
183         if (int documentNodeId = m_instrumentingAgents.inspectorDOMAgent()->boundNodeId(&client->document()))
184             result->addItem(m_instrumentingAgents.inspectorDOMAgent()->pushNodeToFrontend(errorString, documentNodeId, client));
185     }
186 }
187
188 static JSC::JSValue contextAsScriptValue(JSC::ExecState& state, CanvasRenderingContext& context)
189 {
190     JSC::JSLockHolder lock(&state);
191
192     if (is<CanvasRenderingContext2D>(context))
193         return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<CanvasRenderingContext2D>(context));
194 #if ENABLE(WEBGL)
195     if (is<WebGLRenderingContext>(context))
196         return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<WebGLRenderingContext>(context));
197 #endif
198 #if ENABLE(WEBGL2)
199     if (is<WebGL2RenderingContext>(context))
200         return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<WebGL2RenderingContext>(context));
201 #endif
202 #if ENABLE(WEBGPU)
203     if (is<GPUCanvasContext>(context))
204         return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<GPUCanvasContext>(context));
205 #endif
206     if (is<ImageBitmapRenderingContext>(context))
207         return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<ImageBitmapRenderingContext>(context));
208
209     return { };
210 }
211
212 void InspectorCanvasAgent::resolveCanvasContext(ErrorString& errorString, const String& canvasId, const String* objectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result)
213 {
214     auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
215     if (!inspectorCanvas)
216         return;
217
218     auto& state = *inspectorCanvas->context().canvasBase().scriptExecutionContext()->execState();
219     auto injectedScript = m_injectedScriptManager.injectedScriptFor(&state);
220     ASSERT(!injectedScript.hasNoValue());
221
222     JSC::JSValue value = contextAsScriptValue(state, inspectorCanvas->context());
223     if (!value) {
224         ASSERT_NOT_REACHED();
225         errorString = "Unknown context type"_s;
226         return;
227     }
228
229     String objectGroupName = objectGroup ? *objectGroup : String();
230     result = injectedScript.wrapObject(value, objectGroupName);
231 }
232
233 void InspectorCanvasAgent::setRecordingAutoCaptureFrameCount(ErrorString&, int count)
234 {
235     if (count > 0)
236         m_recordingAutoCaptureFrameCount = count;
237     else
238         m_recordingAutoCaptureFrameCount = WTF::nullopt;
239 }
240
241 void InspectorCanvasAgent::startRecording(ErrorString& errorString, const String& canvasId, const int* frameCount, const int* memoryLimit)
242 {
243     auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
244     if (!inspectorCanvas)
245         return;
246
247     if (inspectorCanvas->context().callTracingActive()) {
248         errorString = "Already recording canvas"_s;
249         return;
250     }
251
252     RecordingOptions recordingOptions;
253     if (frameCount)
254         recordingOptions.frameCount = *frameCount;
255     if (memoryLimit)
256         recordingOptions.memoryLimit = *memoryLimit;
257     startRecording(*inspectorCanvas, Inspector::Protocol::Recording::Initiator::Frontend, WTFMove(recordingOptions));
258 }
259
260 void InspectorCanvasAgent::stopRecording(ErrorString& errorString, const String& canvasId)
261 {
262     auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
263     if (!inspectorCanvas)
264         return;
265
266     if (!inspectorCanvas->context().callTracingActive()) {
267         errorString = "No active recording for canvas"_s;
268         return;
269     }
270
271     didFinishRecordingCanvasFrame(inspectorCanvas->context(), 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 = "No shader for given type."_s;
284         return;
285     }
286
287     *content = shader->getSource();
288 #else
289     UNUSED_PARAM(programId);
290     UNUSED_PARAM(shaderType);
291     UNUSED_PARAM(content);
292     errorString = "WebGL is not supported."_s;
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 = "No shader for given type."_s;
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 = "Shader compilation failed."_s;
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 = "WebGL is not supported."_s;
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 = "WebGL is not supported."_s;
339 #endif
340 }
341
342 void InspectorCanvasAgent::setShaderProgramHighlighted(ErrorString& errorString, const String& programId, bool highlighted)
343 {
344 #if ENABLE(WEBGL)
345     auto inspectorProgram = assertInspectorProgram(errorString, programId);
346     if (!inspectorProgram)
347         return;
348
349     inspectorProgram->setHighlighted(highlighted);
350 #else
351     UNUSED_PARAM(programId);
352     UNUSED_PARAM(highlighted);
353     errorString = "WebGL is not supported."_s;
354 #endif
355 }
356
357 void InspectorCanvasAgent::frameNavigated(Frame& frame)
358 {
359     if (frame.isMainFrame()) {
360         clearCanvasData();
361         return;
362     }
363
364     Vector<InspectorCanvas*> inspectorCanvases;
365     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
366         if (auto* canvasElement = inspectorCanvas->canvasElement()) {
367             if (canvasElement->document().frame() == &frame)
368                 inspectorCanvases.append(inspectorCanvas.get());
369         }
370     }
371
372     for (auto* inspectorCanvas : inspectorCanvases) {
373         String identifier = unbindCanvas(*inspectorCanvas);
374         m_frontendDispatcher->canvasRemoved(identifier);
375     }
376 }
377
378 void InspectorCanvasAgent::didChangeCSSCanvasClientNodes(CanvasBase& canvasBase)
379 {
380     auto* context = canvasBase.renderingContext();
381     if (!context) {
382         ASSERT_NOT_REACHED();
383         return;
384     }
385
386     auto inspectorCanvas = findInspectorCanvas(*context);
387     ASSERT(inspectorCanvas);
388     if (!inspectorCanvas)
389         return;
390
391     m_frontendDispatcher->cssCanvasClientNodesChanged(inspectorCanvas->identifier());
392 }
393
394 void InspectorCanvasAgent::didCreateCanvasRenderingContext(CanvasRenderingContext& context)
395 {
396     if (findInspectorCanvas(context)) {
397         ASSERT_NOT_REACHED();
398         return;
399     }
400
401     auto& inspectorCanvas = bindCanvas(context, true);
402
403     if (m_recordingAutoCaptureFrameCount) {
404         RecordingOptions recordingOptions;
405         recordingOptions.frameCount = m_recordingAutoCaptureFrameCount.value();
406         startRecording(inspectorCanvas, Inspector::Protocol::Recording::Initiator::AutoCapture, WTFMove(recordingOptions));
407     }
408 }
409
410 void InspectorCanvasAgent::didChangeCanvasMemory(CanvasRenderingContext& context)
411 {
412     auto inspectorCanvas = findInspectorCanvas(context);
413     ASSERT(inspectorCanvas);
414     if (!inspectorCanvas)
415         return;
416
417     // FIXME: <https://webkit.org/b/180833> Web Inspector: support OffscreenCanvas for Canvas related operations
418
419     if (auto* node = inspectorCanvas->canvasElement())
420         m_frontendDispatcher->canvasMemoryChanged(inspectorCanvas->identifier(), node->memoryCost());
421 }
422
423 void InspectorCanvasAgent::recordCanvasAction(CanvasRenderingContext& canvasRenderingContext, const String& name, std::initializer_list<RecordCanvasActionVariant>&& parameters)
424 {
425     auto inspectorCanvas = findInspectorCanvas(canvasRenderingContext);
426     ASSERT(inspectorCanvas);
427     if (!inspectorCanvas)
428         return;
429
430     ASSERT(canvasRenderingContext.callTracingActive());
431     if (!canvasRenderingContext.callTracingActive())
432         return;
433
434     // Only enqueue a microtask for the first action of each frame. Any subsequent actions will be
435     // covered by the initial microtask until the next frame.
436     if (!inspectorCanvas->currentFrameHasData()) {
437         if (auto* scriptExecutionContext = inspectorCanvas->context().canvasBase().scriptExecutionContext()) {
438             auto& queue = MicrotaskQueue::mainThreadQueue();
439             queue.append(makeUnique<ActiveDOMCallbackMicrotask>(queue, *scriptExecutionContext, [&, protectedInspectorCanvas = inspectorCanvas.copyRef()] {
440                 if (auto* canvasElement = protectedInspectorCanvas->canvasElement()) {
441                     if (canvasElement->isDescendantOf(canvasElement->document()))
442                         return;
443                 }
444
445                 if (protectedInspectorCanvas->context().callTracingActive())
446                     didFinishRecordingCanvasFrame(protectedInspectorCanvas->context());
447             }));
448         }
449     }
450
451     inspectorCanvas->recordAction(name, WTFMove(parameters));
452
453     if (!inspectorCanvas->hasBufferSpace())
454         didFinishRecordingCanvasFrame(inspectorCanvas->context(), true);
455 }
456
457 void InspectorCanvasAgent::canvasChanged(CanvasBase& canvasBase, const FloatRect&)
458 {
459     auto* context = canvasBase.renderingContext();
460     if (!context)
461         return;
462
463     auto inspectorCanvas = findInspectorCanvas(*context);
464     ASSERT(inspectorCanvas);
465     if (!inspectorCanvas)
466         return;
467
468     inspectorCanvas->canvasChanged();
469 }
470
471 void InspectorCanvasAgent::canvasDestroyed(CanvasBase& canvasBase)
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     String identifier = unbindCanvas(*inspectorCanvas);
483
484     // WebCore::CanvasObserver::canvasDestroyed is called in response to the GC destroying the CanvasBase.
485     // Due to the single-process model used in WebKit1, the event must be dispatched from a timer to prevent
486     // the frontend from making JS allocations while the GC is still active.
487     m_removedCanvasIdentifiers.append(identifier);
488
489     if (!m_canvasDestroyedTimer.isActive())
490         m_canvasDestroyedTimer.startOneShot(0_s);
491 }
492
493 void InspectorCanvasAgent::didFinishRecordingCanvasFrame(CanvasRenderingContext& context, bool forceDispatch)
494 {
495     auto inspectorCanvas = findInspectorCanvas(context);
496     ASSERT(inspectorCanvas);
497     if (!inspectorCanvas)
498         return;
499
500     if (!inspectorCanvas->context().callTracingActive())
501         return;
502
503     if (!inspectorCanvas->hasRecordingData()) {
504         if (forceDispatch) {
505             m_frontendDispatcher->recordingFinished(inspectorCanvas->identifier(), nullptr);
506             inspectorCanvas->resetRecordingData();
507         }
508         return;
509     }
510
511     if (forceDispatch)
512         inspectorCanvas->markCurrentFrameIncomplete();
513
514     inspectorCanvas->finalizeFrame();
515     if (inspectorCanvas->currentFrameHasData())
516         m_frontendDispatcher->recordingProgress(inspectorCanvas->identifier(), inspectorCanvas->releaseFrames(), inspectorCanvas->bufferUsed());
517
518     if (!forceDispatch && !inspectorCanvas->overFrameCount())
519         return;
520
521     m_frontendDispatcher->recordingFinished(inspectorCanvas->identifier(), inspectorCanvas->releaseObjectForRecording());
522 }
523
524 void InspectorCanvasAgent::consoleStartRecordingCanvas(CanvasRenderingContext& context, JSC::ExecState& exec, JSC::JSObject* options)
525 {
526     auto inspectorCanvas = findInspectorCanvas(context);
527     ASSERT(inspectorCanvas);
528     if (!inspectorCanvas)
529         return;
530
531     RecordingOptions recordingOptions;
532     if (options) {
533         if (JSC::JSValue optionSingleFrame = options->get(&exec, JSC::Identifier::fromString(&exec, "singleFrame")))
534             recordingOptions.frameCount = optionSingleFrame.toBoolean(&exec) ? 1 : 0;
535         if (JSC::JSValue optionFrameCount = options->get(&exec, JSC::Identifier::fromString(&exec, "frameCount")))
536             recordingOptions.frameCount = optionFrameCount.toNumber(&exec);
537         if (JSC::JSValue optionMemoryLimit = options->get(&exec, JSC::Identifier::fromString(&exec, "memoryLimit")))
538             recordingOptions.memoryLimit = optionMemoryLimit.toNumber(&exec);
539         if (JSC::JSValue optionName = options->get(&exec, JSC::Identifier::fromString(&exec, "name")))
540             recordingOptions.name = optionName.toWTFString(&exec);
541     }
542     startRecording(*inspectorCanvas, Inspector::Protocol::Recording::Initiator::Console, WTFMove(recordingOptions));
543 }
544
545 #if ENABLE(WEBGL)
546 void InspectorCanvasAgent::didEnableExtension(WebGLRenderingContextBase& context, const String& extension)
547 {
548     auto inspectorCanvas = findInspectorCanvas(context);
549     ASSERT(inspectorCanvas);
550     if (!inspectorCanvas)
551         return;
552
553     m_frontendDispatcher->extensionEnabled(inspectorCanvas->identifier(), extension);
554 }
555
556 void InspectorCanvasAgent::didCreateProgram(WebGLRenderingContextBase& context, WebGLProgram& program)
557 {
558     auto inspectorCanvas = findInspectorCanvas(context);
559     ASSERT(inspectorCanvas);
560     if (!inspectorCanvas)
561         return;
562
563     auto inspectorProgram = InspectorShaderProgram::create(program, *inspectorCanvas);
564     String programIdentifier = inspectorProgram->identifier();
565     m_identifierToInspectorProgram.set(programIdentifier, WTFMove(inspectorProgram));
566     m_frontendDispatcher->programCreated(inspectorCanvas->identifier(), programIdentifier);
567 }
568
569 void InspectorCanvasAgent::willDeleteProgram(WebGLProgram& program)
570 {
571     auto inspectorProgram = findInspectorProgram(program);
572     if (!inspectorProgram)
573         return;
574
575     String identifier = unbindProgram(*inspectorProgram);
576     m_frontendDispatcher->programDeleted(identifier);
577 }
578
579 bool InspectorCanvasAgent::isShaderProgramDisabled(WebGLProgram& program)
580 {
581     auto inspectorProgram = findInspectorProgram(program);
582     ASSERT(inspectorProgram);
583     if (!inspectorProgram)
584         return false;
585
586     return inspectorProgram->disabled();
587 }
588
589 bool InspectorCanvasAgent::isShaderProgramHighlighted(WebGLProgram& program)
590 {
591     auto inspectorProgram = findInspectorProgram(program);
592     ASSERT(inspectorProgram);
593     if (!inspectorProgram)
594         return false;
595
596     return inspectorProgram->highlighted();
597 }
598 #endif
599
600 void InspectorCanvasAgent::startRecording(InspectorCanvas& inspectorCanvas, Inspector::Protocol::Recording::Initiator initiator, RecordingOptions&& recordingOptions)
601 {
602     auto& canvasRenderingContext = inspectorCanvas.context();
603
604     if (!is<CanvasRenderingContext2D>(canvasRenderingContext)
605 #if ENABLE(WEBGL)
606         && !is<WebGLRenderingContext>(canvasRenderingContext)
607 #endif
608 #if ENABLE(WEBGL2)
609         && !is<WebGL2RenderingContext>(canvasRenderingContext)
610 #endif
611         && !is<ImageBitmapRenderingContext>(canvasRenderingContext))
612         return;
613
614     if (canvasRenderingContext.callTracingActive())
615         return;
616
617     inspectorCanvas.resetRecordingData();
618     if (recordingOptions.frameCount)
619         inspectorCanvas.setFrameCount(recordingOptions.frameCount.value());
620     if (recordingOptions.memoryLimit)
621         inspectorCanvas.setBufferLimit(recordingOptions.memoryLimit.value());
622     if (recordingOptions.name)
623         inspectorCanvas.setRecordingName(recordingOptions.name.value());
624     canvasRenderingContext.setCallTracingActive(true);
625
626     m_frontendDispatcher->recordingStarted(inspectorCanvas.identifier(), initiator);
627 }
628
629 void InspectorCanvasAgent::canvasDestroyedTimerFired()
630 {
631     if (!m_removedCanvasIdentifiers.size())
632         return;
633
634     for (auto& identifier : m_removedCanvasIdentifiers)
635         m_frontendDispatcher->canvasRemoved(identifier);
636
637     m_removedCanvasIdentifiers.clear();
638 }
639
640 void InspectorCanvasAgent::clearCanvasData()
641 {
642     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values())
643         inspectorCanvas->context().canvasBase().removeObserver(*this);
644
645     m_identifierToInspectorCanvas.clear();
646 #if ENABLE(WEBGL)
647     m_identifierToInspectorProgram.clear();
648     m_removedCanvasIdentifiers.clear();
649 #endif
650
651     if (m_canvasDestroyedTimer.isActive())
652         m_canvasDestroyedTimer.stop();
653 }
654
655 InspectorCanvas& InspectorCanvasAgent::bindCanvas(CanvasRenderingContext& context, bool captureBacktrace)
656 {
657     auto inspectorCanvas = InspectorCanvas::create(context);
658     m_identifierToInspectorCanvas.set(inspectorCanvas->identifier(), inspectorCanvas.copyRef());
659
660     inspectorCanvas->context().canvasBase().addObserver(*this);
661
662     m_frontendDispatcher->canvasAdded(inspectorCanvas->buildObjectForCanvas(captureBacktrace));
663
664 #if ENABLE(WEBGL)
665     if (is<WebGLRenderingContextBase>(inspectorCanvas->context())) {
666         WebGLRenderingContextBase& contextWebGL = downcast<WebGLRenderingContextBase>(inspectorCanvas->context());
667         if (Optional<Vector<String>> extensions = contextWebGL.getSupportedExtensions()) {
668             for (const String& extension : *extensions) {
669                 if (contextWebGL.extensionIsEnabled(extension))
670                     m_frontendDispatcher->extensionEnabled(inspectorCanvas->identifier(), extension);
671             }
672         }
673     }
674 #endif
675
676     return inspectorCanvas;
677 }
678
679 String InspectorCanvasAgent::unbindCanvas(InspectorCanvas& inspectorCanvas)
680 {
681 #if ENABLE(WEBGL)
682     Vector<InspectorShaderProgram*> programsToRemove;
683     for (auto& inspectorProgram : m_identifierToInspectorProgram.values()) {
684         if (&inspectorProgram->canvas() == &inspectorCanvas)
685             programsToRemove.append(inspectorProgram.get());
686     }
687
688     for (auto* inspectorProgram : programsToRemove)
689         unbindProgram(*inspectorProgram);
690 #endif
691
692     inspectorCanvas.context().canvasBase().removeObserver(*this);
693
694     String identifier = inspectorCanvas.identifier();
695     m_identifierToInspectorCanvas.remove(identifier);
696
697     return identifier;
698 }
699
700 RefPtr<InspectorCanvas> InspectorCanvasAgent::assertInspectorCanvas(ErrorString& errorString, const String& identifier)
701 {
702     auto inspectorCanvas = m_identifierToInspectorCanvas.get(identifier);
703     if (!inspectorCanvas) {
704         errorString = "No canvas for given identifier."_s;
705         return nullptr;
706     }
707
708     return inspectorCanvas;
709 }
710
711 RefPtr<InspectorCanvas> InspectorCanvasAgent::findInspectorCanvas(CanvasRenderingContext& context)
712 {
713     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
714         if (&inspectorCanvas->context() == &context)
715             return inspectorCanvas;
716     }
717
718     return nullptr;
719 }
720
721 #if ENABLE(WEBGL)
722 String InspectorCanvasAgent::unbindProgram(InspectorShaderProgram& inspectorProgram)
723 {
724     String identifier = inspectorProgram.identifier();
725     m_identifierToInspectorProgram.remove(identifier);
726
727     return identifier;
728 }
729
730 RefPtr<InspectorShaderProgram> InspectorCanvasAgent::assertInspectorProgram(ErrorString& errorString, const String& identifier)
731 {
732     auto inspectorProgram = m_identifierToInspectorProgram.get(identifier);
733     if (!inspectorProgram) {
734         errorString = "No shader program for given identifier."_s;
735         return nullptr;
736     }
737
738     return inspectorProgram;
739 }
740
741 RefPtr<InspectorShaderProgram> InspectorCanvasAgent::findInspectorProgram(WebGLProgram& program)
742 {
743     for (auto& inspectorProgram : m_identifierToInspectorProgram.values()) {
744         if (&inspectorProgram->program() == &program)
745             return inspectorProgram;
746     }
747
748     return nullptr;
749 }
750 #endif
751
752 } // namespace WebCore