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
68namespace WebCore {
69
70using namespace Inspector;
71
72InspectorCanvasAgent::InspectorCanvasAgent(PageAgentContext& context)
73 : InspectorAgentBase("Canvas"_s, context)
74 , m_frontendDispatcher(std::make_unique<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
82void InspectorCanvasAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
83{
84}
85
86void InspectorCanvasAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
87{
88 ErrorString ignored;
89 disable(ignored);
90}
91
92void InspectorCanvasAgent::discardAgent()
93{
94 clearCanvasData();
95}
96
97void 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
136void InspectorCanvasAgent::disable(ErrorString&)
137{
138 m_instrumentingAgents.setInspectorCanvasAgent(nullptr);
139
140 clearCanvasData();
141
142 m_recordingAutoCaptureFrameCount = WTF::nullopt;
143}
144
145void 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
166void 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
175void 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
188static 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
212void 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
233void InspectorCanvasAgent::setRecordingAutoCaptureFrameCount(ErrorString&, int count)
234{
235 if (count > 0)
236 m_recordingAutoCaptureFrameCount = count;
237 else
238 m_recordingAutoCaptureFrameCount = WTF::nullopt;
239}
240
241void 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
260void 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
274void 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
296void 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
327void 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
342void 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
357void 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
378void 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
394void 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
410void 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
423void InspectorCanvasAgent::recordCanvasAction(CanvasRenderingContext& canvasRenderingContext, const String& name, Vector<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(std::make_unique<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
457void 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
471void 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
493void 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
524void 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)
546void 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
556void 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
569void 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
579bool 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
589bool 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
600void 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
629void 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
640void 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
655InspectorCanvas& 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
679String 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
700RefPtr<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
711RefPtr<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)
722String InspectorCanvasAgent::unbindProgram(InspectorShaderProgram& inspectorProgram)
723{
724 String identifier = inspectorProgram.identifier();
725 m_identifierToInspectorProgram.remove(identifier);
726
727 return identifier;
728}
729
730RefPtr<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
741RefPtr<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
753