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