Web Inspector: Safari Canvas Inspector seems to show the canvas being rendered twice...
authordrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Mar 2019 02:47:35 +0000 (02:47 +0000)
committerdrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Mar 2019 02:47:35 +0000 (02:47 +0000)
https://bugs.webkit.org/show_bug.cgi?id=196082
<rdar://problem/49113496>

Reviewed by Dean Jackson.

Source/WebCore:

Tests: inspector/canvas/recording-2d.html
       inspector/canvas/recording-bitmaprenderer.html
       inspector/canvas/recording-html-2d.html
       inspector/canvas/recording-webgl.html
       inspector/canvas/setRecordingAutoCaptureFrameCount.html

WebGL `<canvas>` follow a different "rendering" path such that `HTMLCanvasElement::paint`
isn't called. Previously, there was a 0s timer that was started after the first action of a
frame was recorded (for the case that the `<canvas>` isn't attached to the DOM) that would
automatically stop the recording. It was possible that actions in two different "frame"s
were recorded as part of the same frame, because the WebGL `<canvas>` would instead fall
back to the timer to know when the "frame" had ended.

Now, there is additional instrumentation for the WebGL specific rendering path.
Additionally, replace the 0s timer with a microtask for more "immediate" calling.

* html/HTMLCanvasElement.cpp:
(WebCore::HTMLCanvasElement::paint):
Ensure that the `InspectorInstrumentation` call is last. This matches what we expect, as
before we were instrumenting right before is it about to paint.

* platform/graphics/GraphicsContext3D.h:
(WebCore::GraphicsContext3D::Client::~Client): Added.
(WebCore::GraphicsContext3D::addClient): Added.
(WebCore::GraphicsContext3D::removeClient): Added.
(WebCore::GraphicsContext3D::setWebGLContext): Deleted.
* platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp:
(WebCore::GraphicsContext3D::markLayerComposited):
(WebCore::GraphicsContext3D::forceContextLost):
(WebCore::GraphicsContext3D::recycleContext):
(WebCore::GraphicsContext3D::dispatchContextChangedNotification):
* html/canvas/WebGLRenderingContextBase.h:
* html/canvas/WebGLRenderingContextBase.cpp:
(WebCore::WebGLRenderingContextBase::WebGLRenderingContextBase):
(WebCore::WebGLRenderingContextBase::destroyGraphicsContext3D):
(WebCore::WebGLRenderingContextBase::didComposite): Added.
(WebCore::WebGLRenderingContextBase::forceContextLost):
(WebCore::WebGLRenderingContextBase::recycleContext):
(WebCore::WebGLRenderingContextBase::dispatchContextChangedNotification): Added.
(WebCore::WebGLRenderingContextBase::dispatchContextChangedEvent): Deleted.
Introduce a `GraphicsContext3DClient` abstract class, rather than passing the
`WebGLRenderingContextBase` directly to the `GraphicsContext3D` (layering violation).
Notify the client whenever the `GraphicsContext3D` composites, which will in turn notify the
`InspectorCanvasAgent` so that it knows that the "frame" is over.

* inspector/agents/InspectorCanvasAgent.h:
* inspector/agents/InspectorCanvasAgent.cpp:
(WebCore::InspectorCanvasAgent::InspectorCanvasAgent):
(WebCore::InspectorCanvasAgent::requestNode):
(WebCore::InspectorCanvasAgent::requestContent):
(WebCore::InspectorCanvasAgent::requestCSSCanvasClientNodes):
(WebCore::InspectorCanvasAgent::resolveCanvasContext):
(WebCore::InspectorCanvasAgent::startRecording):
(WebCore::InspectorCanvasAgent::stopRecording):
(WebCore::InspectorCanvasAgent::requestShaderSource):
(WebCore::InspectorCanvasAgent::updateShader):
(WebCore::InspectorCanvasAgent::setShaderProgramDisabled):
(WebCore::InspectorCanvasAgent::setShaderProgramHighlighted):
(WebCore::InspectorCanvasAgent::didChangeCSSCanvasClientNodes):
(WebCore::InspectorCanvasAgent::didChangeCanvasMemory):
(WebCore::InspectorCanvasAgent::recordCanvasAction):
(WebCore::InspectorCanvasAgent::canvasDestroyed):
(WebCore::InspectorCanvasAgent::didFinishRecordingCanvasFrame):
(WebCore::InspectorCanvasAgent::consoleStartRecordingCanvas):
(WebCore::InspectorCanvasAgent::didEnableExtension):
(WebCore::InspectorCanvasAgent::didCreateProgram):
(WebCore::InspectorCanvasAgent::willDeleteProgram):
(WebCore::InspectorCanvasAgent::isShaderProgramDisabled):
(WebCore::InspectorCanvasAgent::isShaderProgramHighlighted):
(WebCore::InspectorCanvasAgent::clearCanvasData):
(WebCore::InspectorCanvasAgent::assertInspectorCanvas):
(WebCore::InspectorCanvasAgent::findInspectorCanvas):
(WebCore::InspectorCanvasAgent::assertInspectorProgram):
(WebCore::InspectorCanvasAgent::findInspectorProgram):
(WebCore::InspectorCanvasAgent::canvasRecordingTimerFired): Deleted.
Replace raw pointers with `RefPtr`s. This is primarily used so that the microtask (instead
of a timer) that is enqueued after the first action of each frame  is recorded can access a
ref-counted instance of an `InspectorCanvas`, ensuring that it isn't destructed.

* inspector/InspectorCanvas.h:
* inspector/InspectorCanvas.cpp:
(WebCore::InspectorCanvas::canvasElement):
(WebCore::InspectorCanvas::recordAction):
(WebCore::InspectorCanvas::finalizeFrame):
(WebCore::InspectorCanvas::releaseObjectForRecording): Added.
(WebCore::InspectorCanvas::getCanvasContentAsDataURL):
(WebCore::InspectorCanvas::appendActionSnapshotIfNeeded):
(WebCore::InspectorCanvas::buildInitialState):
(WebCore::InspectorCanvas::releaseInitialState): Deleted.
(WebCore::InspectorCanvas::releaseFrames): Deleted.
(WebCore::InspectorCanvas::releaseData): Deleted.
Move the recording payload construction logic to `InspectorCanvas` so the actual data
doesn't need to leave that class.
Drive-by: unify the logic for getting the contents of a canvas from `InspectorCanvasAgent`.
LayoutTests:

* inspector/canvas/recording-2d.html:
* inspector/canvas/recording-bitmaprenderer.html:
* inspector/canvas/recording-html-2d.html:
* inspector/canvas/recording-webgl.html:
* inspector/canvas/setRecordingAutoCaptureFrameCount.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@243356 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/canvas/recording-2d.html
LayoutTests/inspector/canvas/recording-bitmaprenderer.html
LayoutTests/inspector/canvas/recording-html-2d.html
LayoutTests/inspector/canvas/recording-webgl.html
LayoutTests/inspector/canvas/setRecordingAutoCaptureFrameCount.html
Source/WebCore/ChangeLog
Source/WebCore/html/HTMLCanvasElement.cpp
Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp
Source/WebCore/html/canvas/WebGLRenderingContextBase.h
Source/WebCore/inspector/InspectorCanvas.cpp
Source/WebCore/inspector/InspectorCanvas.h
Source/WebCore/inspector/agents/InspectorCanvasAgent.cpp
Source/WebCore/inspector/agents/InspectorCanvasAgent.h
Source/WebCore/platform/graphics/GraphicsContext3D.h
Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp

index 9178aaa..aaf5378 100644 (file)
@@ -1,3 +1,17 @@
+2019-03-21  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Safari Canvas Inspector seems to show the canvas being rendered twice per frame.
+        https://bugs.webkit.org/show_bug.cgi?id=196082
+        <rdar://problem/49113496>
+
+        Reviewed by Dean Jackson.
+
+        * inspector/canvas/recording-2d.html:
+        * inspector/canvas/recording-bitmaprenderer.html:
+        * inspector/canvas/recording-html-2d.html:
+        * inspector/canvas/recording-webgl.html:
+        * inspector/canvas/setRecordingAutoCaptureFrameCount.html:
+
 2019-03-21  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: Timelines - Cannot export on about:blank - suggested filename containing a colon silently fails
index 0ca9673..df20f0b 100644 (file)
@@ -39,8 +39,6 @@ async function load() {
     pattern = ctx.createPattern(image, "no-repeat");
     bitmap = await createImageBitmap(image);
 
-    document.body.appendChild(canvas);
-
     ctx.save();
     cancelActions();
 
@@ -53,7 +51,7 @@ function ignoreException(func){
     } catch (e) { }
 }
 
-let timeoutID = NaN;
+let requestAnimationFrameId = NaN;
 let saveCount = 1;
 
 function cancelActions() {
@@ -61,8 +59,8 @@ function cancelActions() {
         ctx.restore();
     ctx.restore(); // Ensures the state is reset between test cases.
 
-    clearTimeout(timeoutID);
-    timeoutID = NaN;
+    cancelAnimationFrame(requestAnimationFrameId);
+    requestAnimationFrameId = NaN;
 
     ctx.save(); // Ensures the state is reset between test cases.
     ctx.save(); // This matches the `restore` call in `performActions`.
@@ -399,7 +397,7 @@ function performActions() {
     function executeFrameFunction() {
         frames[index++]();
         if (index < frames.length)
-            timeoutID = setTimeout(executeFrameFunction, 0);
+            requestAnimationFrameId = requestAnimationFrame(executeFrameFunction);
     };
     executeFrameFunction();
 }
index 3b0bff4..5c1055f 100644 (file)
@@ -23,7 +23,7 @@ canvas.height = 2;
 function load() {
     ctx = canvas.getContext("bitmaprenderer");
 
-    document.body.appendChild(canvas);
+    cancelActions();
 
     runTest();
 }
@@ -34,11 +34,11 @@ function ignoreException(func){
     } catch (e) { }
 }
 
-let timeoutID = NaN;
+let requestAnimationFrameId = NaN;
 
 function cancelActions() {
-    clearTimeout(timeoutID);
-    timeoutID = NaN;
+    cancelAnimationFrame(requestAnimationFrameId);
+    requestAnimationFrameId = NaN;
 
     createImageBitmap(transparentImage).then((transparentBitmap) => {
         ctx.transferFromImageBitmap(transparentBitmap);
@@ -68,7 +68,7 @@ async function performActions() {
     function executeFrameFunction() {
         frames[index++]();
         if (index < frames.length)
-            timeoutID = setTimeout(executeFrameFunction, 0);
+            requestAnimationFrameId = requestAnimationFrame(executeFrameFunction);
     };
     executeFrameFunction();
 }
index f4b6cd3..6d7a403 100644 (file)
@@ -39,7 +39,7 @@ async function load() {
 
     imageBitmap = await createImageBitmap(image);
 
-    document.body.appendChild(canvas);
+    cancelActions();
 
     context.strokeStyle = "red";
     context.save();
@@ -56,7 +56,15 @@ function ignoreException(func){
     } catch (e) { }
 }
 
+let requestAnimationFrameId = NaN;
+
 function cancelActions() {
+    cancelAnimationFrame(requestAnimationFrameId);
+    requestAnimationFrameId = NaN;
+
+    context.resetTransform();
+    context.beginPath();
+    context.clearRect(0, 0, context.canvas.width, context.canvas.height);
 }
 
 function performActions() {
@@ -97,7 +105,7 @@ function performActions() {
     function executeFrameFunction() {
         frames[index++]();
         if (index < frames.length)
-            timeoutID = setTimeout(executeFrameFunction, 0);
+            requestAnimationFrameId = requestAnimationFrame(executeFrameFunction);
     };
     executeFrameFunction();
 }
index 5867438..7672d76 100644 (file)
@@ -49,7 +49,7 @@ function load() {
     shader = context.createShader(context.VERTEX_SHADER);
     texture = context.createTexture();
 
-    document.body.appendChild(context.canvas);
+    cancelActions();
 
     runTest();
 }
@@ -60,11 +60,11 @@ function ignoreException(func){
     } catch (e) { }
 }
 
-let timeoutID = NaN;
+let requestAnimationFrameId = NaN;
 
 function cancelActions() {
-    clearTimeout(timeoutID);
-    timeoutID = NaN;
+    cancelAnimationFrame(requestAnimationFrameId);
+    requestAnimationFrameId = NaN;
 
     context.clearColor(0.0, 0.0, 0.0, 0.0);
     context.clear(context.COLOR_BUFFER_BIT);
@@ -502,7 +502,7 @@ function performActions() {
     function executeFrameFunction() {
         frames[index++]();
         if (index < frames.length)
-            timeoutID = setTimeout(executeFrameFunction, 0);
+            requestAnimationFrameId = requestAnimationFrame(executeFrameFunction);
     };
     executeFrameFunction();
 }
index 040c21d..d98f0fe 100644 (file)
@@ -6,11 +6,11 @@
 if (window.internals)
     window.internals.settings.setWebGLErrorsToConsoleEnabled(false);
 
-let timeoutID = NaN;
+let requestAnimationFrameId = NaN;
 
 function cancelActions() {
-    clearTimeout(timeoutID);
-    timeoutID = NaN;
+    cancelAnimationFrame(requestAnimationFrameId);
+    requestAnimationFrameId = NaN;
 }
 
 function performActions(frames) {
@@ -19,7 +19,7 @@ function performActions(frames) {
         frames[index++]();
 
         if (index < frames.length)
-            timeoutID = setTimeout(executeFrameFunction, 0);
+            requestAnimationFrameId = requestAnimationFrame(executeFrameFunction);
         else {
             setTimeout(() => {
                 TestPage.dispatchEventToFrontend("LastFrame");
index ffd93c5..5635df8 100644 (file)
@@ -1,3 +1,106 @@
+2019-03-21  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Safari Canvas Inspector seems to show the canvas being rendered twice per frame.
+        https://bugs.webkit.org/show_bug.cgi?id=196082
+        <rdar://problem/49113496>
+
+        Reviewed by Dean Jackson.
+
+        Tests: inspector/canvas/recording-2d.html
+               inspector/canvas/recording-bitmaprenderer.html
+               inspector/canvas/recording-html-2d.html
+               inspector/canvas/recording-webgl.html
+               inspector/canvas/setRecordingAutoCaptureFrameCount.html
+
+        WebGL `<canvas>` follow a different "rendering" path such that `HTMLCanvasElement::paint`
+        isn't called. Previously, there was a 0s timer that was started after the first action of a
+        frame was recorded (for the case that the `<canvas>` isn't attached to the DOM) that would
+        automatically stop the recording. It was possible that actions in two different "frame"s
+        were recorded as part of the same frame, because the WebGL `<canvas>` would instead fall
+        back to the timer to know when the "frame" had ended.
+
+        Now, there is additional instrumentation for the WebGL specific rendering path.
+        Additionally, replace the 0s timer with a microtask for more "immediate" calling.
+
+        * html/HTMLCanvasElement.cpp:
+        (WebCore::HTMLCanvasElement::paint):
+        Ensure that the `InspectorInstrumentation` call is last. This matches what we expect, as
+        before we were instrumenting right before is it about to paint.
+
+        * platform/graphics/GraphicsContext3D.h:
+        (WebCore::GraphicsContext3D::Client::~Client): Added.
+        (WebCore::GraphicsContext3D::addClient): Added.
+        (WebCore::GraphicsContext3D::removeClient): Added.
+        (WebCore::GraphicsContext3D::setWebGLContext): Deleted.
+        * platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp:
+        (WebCore::GraphicsContext3D::markLayerComposited):
+        (WebCore::GraphicsContext3D::forceContextLost):
+        (WebCore::GraphicsContext3D::recycleContext):
+        (WebCore::GraphicsContext3D::dispatchContextChangedNotification):
+        * html/canvas/WebGLRenderingContextBase.h:
+        * html/canvas/WebGLRenderingContextBase.cpp:
+        (WebCore::WebGLRenderingContextBase::WebGLRenderingContextBase):
+        (WebCore::WebGLRenderingContextBase::destroyGraphicsContext3D):
+        (WebCore::WebGLRenderingContextBase::didComposite): Added.
+        (WebCore::WebGLRenderingContextBase::forceContextLost):
+        (WebCore::WebGLRenderingContextBase::recycleContext):
+        (WebCore::WebGLRenderingContextBase::dispatchContextChangedNotification): Added.
+        (WebCore::WebGLRenderingContextBase::dispatchContextChangedEvent): Deleted.
+        Introduce a `GraphicsContext3DClient` abstract class, rather than passing the
+        `WebGLRenderingContextBase` directly to the `GraphicsContext3D` (layering violation).
+        Notify the client whenever the `GraphicsContext3D` composites, which will in turn notify the
+        `InspectorCanvasAgent` so that it knows that the "frame" is over.
+
+        * inspector/agents/InspectorCanvasAgent.h:
+        * inspector/agents/InspectorCanvasAgent.cpp:
+        (WebCore::InspectorCanvasAgent::InspectorCanvasAgent):
+        (WebCore::InspectorCanvasAgent::requestNode):
+        (WebCore::InspectorCanvasAgent::requestContent):
+        (WebCore::InspectorCanvasAgent::requestCSSCanvasClientNodes):
+        (WebCore::InspectorCanvasAgent::resolveCanvasContext):
+        (WebCore::InspectorCanvasAgent::startRecording):
+        (WebCore::InspectorCanvasAgent::stopRecording):
+        (WebCore::InspectorCanvasAgent::requestShaderSource):
+        (WebCore::InspectorCanvasAgent::updateShader):
+        (WebCore::InspectorCanvasAgent::setShaderProgramDisabled):
+        (WebCore::InspectorCanvasAgent::setShaderProgramHighlighted):
+        (WebCore::InspectorCanvasAgent::didChangeCSSCanvasClientNodes):
+        (WebCore::InspectorCanvasAgent::didChangeCanvasMemory):
+        (WebCore::InspectorCanvasAgent::recordCanvasAction):
+        (WebCore::InspectorCanvasAgent::canvasDestroyed):
+        (WebCore::InspectorCanvasAgent::didFinishRecordingCanvasFrame):
+        (WebCore::InspectorCanvasAgent::consoleStartRecordingCanvas):
+        (WebCore::InspectorCanvasAgent::didEnableExtension):
+        (WebCore::InspectorCanvasAgent::didCreateProgram):
+        (WebCore::InspectorCanvasAgent::willDeleteProgram):
+        (WebCore::InspectorCanvasAgent::isShaderProgramDisabled):
+        (WebCore::InspectorCanvasAgent::isShaderProgramHighlighted):
+        (WebCore::InspectorCanvasAgent::clearCanvasData):
+        (WebCore::InspectorCanvasAgent::assertInspectorCanvas):
+        (WebCore::InspectorCanvasAgent::findInspectorCanvas):
+        (WebCore::InspectorCanvasAgent::assertInspectorProgram):
+        (WebCore::InspectorCanvasAgent::findInspectorProgram):
+        (WebCore::InspectorCanvasAgent::canvasRecordingTimerFired): Deleted.
+        Replace raw pointers with `RefPtr`s. This is primarily used so that the microtask (instead
+        of a timer) that is enqueued after the first action of each frame  is recorded can access a
+        ref-counted instance of an `InspectorCanvas`, ensuring that it isn't destructed.
+
+        * inspector/InspectorCanvas.h:
+        * inspector/InspectorCanvas.cpp:
+        (WebCore::InspectorCanvas::canvasElement):
+        (WebCore::InspectorCanvas::recordAction):
+        (WebCore::InspectorCanvas::finalizeFrame):
+        (WebCore::InspectorCanvas::releaseObjectForRecording): Added.
+        (WebCore::InspectorCanvas::getCanvasContentAsDataURL):
+        (WebCore::InspectorCanvas::appendActionSnapshotIfNeeded):
+        (WebCore::InspectorCanvas::buildInitialState):
+        (WebCore::InspectorCanvas::releaseInitialState): Deleted.
+        (WebCore::InspectorCanvas::releaseFrames): Deleted.
+        (WebCore::InspectorCanvas::releaseData): Deleted.
+        Move the recording payload construction logic to `InspectorCanvas` so the actual data
+        doesn't need to leave that class.
+        Drive-by: unify the logic for getting the contents of a canvas from `InspectorCanvasAgent`.
+
 2019-03-21  Tim Horton  <timothy_horton@apple.com>
 
         Adopt UIWKDocumentContext
index 665615f..b5981cf 100644 (file)
@@ -668,38 +668,40 @@ bool HTMLCanvasElement::paintsIntoCanvasBuffer() const
 
 void HTMLCanvasElement::paint(GraphicsContext& context, const LayoutRect& r)
 {
-    if (UNLIKELY(m_context && m_context->callTracingActive()))
-        InspectorInstrumentation::didFinishRecordingCanvasFrame(*m_context);
-
     // Clear the dirty rect
     m_dirtyRect = FloatRect();
 
-    if (context.paintingDisabled())
-        return;
-    
-    if (m_context) {
-        if (!paintsIntoCanvasBuffer() && !document().printing())
-            return;
+    if (!context.paintingDisabled()) {
+        bool shouldPaint = true;
 
-        m_context->paintRenderingResultsToCanvas();
-    }
+        if (m_context) {
+            shouldPaint = paintsIntoCanvasBuffer() || document().printing();
+            if (shouldPaint)
+                m_context->paintRenderingResultsToCanvas();
+        }
 
-    if (hasCreatedImageBuffer()) {
-        ImageBuffer* imageBuffer = buffer();
-        if (imageBuffer) {
-            if (m_presentedImage) {
-                ImageOrientationDescription orientationDescription;
+        if (shouldPaint) {
+            if (hasCreatedImageBuffer()) {
+                ImageBuffer* imageBuffer = buffer();
+                if (imageBuffer) {
+                    if (m_presentedImage) {
+                        ImageOrientationDescription orientationDescription;
 #if ENABLE(CSS_IMAGE_ORIENTATION)
-                orientationDescription.setImageOrientationEnum(renderer()->style().imageOrientation());
-#endif 
-                context.drawImage(*m_presentedImage, snappedIntRect(r), ImagePaintingOptions(orientationDescription));
-            } else
-                context.drawImageBuffer(*imageBuffer, snappedIntRect(r));
+                        orientationDescription.setImageOrientationEnum(renderer()->style().imageOrientation());
+#endif
+                        context.drawImage(*m_presentedImage, snappedIntRect(r), ImagePaintingOptions(orientationDescription));
+                    } else
+                        context.drawImageBuffer(*imageBuffer, snappedIntRect(r));
+                }
+            }
+
+            if (isGPUBased())
+                downcast<GPUBasedCanvasRenderingContext>(*m_context).markLayerComposited();
         }
     }
 
-    if (isGPUBased())
-        downcast<GPUBasedCanvasRenderingContext>(*m_context).markLayerComposited();
+    if (UNLIKELY(m_context && m_context->callTracingActive()))
+        InspectorInstrumentation::didFinishRecordingCanvasFrame(*m_context);
 }
 
 bool HTMLCanvasElement::isGPUBased() const
index f52158f..1c0f58a 100644 (file)
@@ -661,7 +661,7 @@ WebGLRenderingContextBase::WebGLRenderingContextBase(CanvasBase& canvas, Ref<Gra
     m_contextGroup = WebGLContextGroup::create();
     m_contextGroup->addContext(*this);
     
-    m_context->setWebGLContext(this);
+    m_context->addClient(*this);
 
     m_context->getIntegerv(GraphicsContext3D::MAX_VIEWPORT_DIMS, m_maxViewportDims);
 
@@ -914,6 +914,7 @@ void WebGLRenderingContextBase::destroyGraphicsContext3D()
     removeActivityStateChangeObserver();
 
     if (m_context) {
+        m_context->removeClient(*this);
         m_context->setContextLostCallback(nullptr);
         m_context->setErrorMessageCallback(nullptr);
         m_context = nullptr;
@@ -5042,15 +5043,6 @@ void WebGLRenderingContextBase::forceLostContext(WebGLRenderingContextBase::Lost
     m_contextGroup->loseContextGroup(mode);
 }
 
-void WebGLRenderingContextBase::recycleContext()
-{
-    printToConsole(MessageLevel::Error, "There are too many active WebGL contexts on this page, the oldest context will be lost.");
-    // Using SyntheticLostContext means the developer won't be able to force the restoration
-    // of the context by calling preventDefault() in a "webglcontextlost" event handler.
-    forceLostContext(SyntheticLostContext);
-    destroyGraphicsContext3D();
-}
-
 void WebGLRenderingContextBase::loseContextImpl(WebGLRenderingContextBase::LostContextMode mode)
 {
     if (isContextLost())
@@ -6225,15 +6217,6 @@ void WebGLRenderingContextBase::maybeRestoreContext()
     canvas->dispatchEvent(WebGLContextEvent::create(eventNames().webglcontextrestoredEvent, Event::CanBubble::No, Event::IsCancelable::Yes, emptyString()));
 }
 
-void WebGLRenderingContextBase::dispatchContextChangedEvent()
-{
-    auto* canvas = htmlCanvas();
-    if (!canvas)
-        return;
-
-    canvas->dispatchEvent(WebGLContextEvent::create(eventNames().webglcontextchangedEvent, Event::CanBubble::No, Event::IsCancelable::Yes, emptyString()));
-}
-
 void WebGLRenderingContextBase::simulateContextChanged()
 {
     if (m_context)
@@ -6513,6 +6496,36 @@ void WebGLRenderingContextBase::setFailNextGPUStatusCheck()
     m_context->setFailNextGPUStatusCheck();
 }
 
+void WebGLRenderingContextBase::didComposite()
+{
+    if (UNLIKELY(callTracingActive()))
+        InspectorInstrumentation::didFinishRecordingCanvasFrame(*this);
+}
+
+void WebGLRenderingContextBase::forceContextLost()
+{
+    forceLostContext(WebGLRenderingContextBase::RealLostContext);
+}
+
+void WebGLRenderingContextBase::recycleContext()
+{
+    printToConsole(MessageLevel::Error, "There are too many active WebGL contexts on this page, the oldest context will be lost.");
+    // Using SyntheticLostContext means the developer won't be able to force the restoration
+    // of the context by calling preventDefault() in a "webglcontextlost" event handler.
+    forceLostContext(SyntheticLostContext);
+    destroyGraphicsContext3D();
+}
+
+void WebGLRenderingContextBase::dispatchContextChangedNotification()
+{
+    auto* canvas = htmlCanvas();
+    if (!canvas)
+        return;
+
+    canvas->dispatchEvent(WebGLContextEvent::create(eventNames().webglcontextchangedEvent, Event::CanBubble::No, Event::IsCancelable::Yes, emptyString()));
+}
+
+
 } // namespace WebCore
 
 #endif // ENABLE(WEBGL)
index beb59b5..c7382cb 100644 (file)
@@ -94,7 +94,7 @@ class HTMLVideoElement;
 
 using WebGLCanvas = WTF::Variant<RefPtr<HTMLCanvasElement>, RefPtr<OffscreenCanvas>>;
 
-class WebGLRenderingContextBase : public GPUBasedCanvasRenderingContext, private ActivityStateChangeObserver {
+class WebGLRenderingContextBase : public GraphicsContext3D::Client, public GPUBasedCanvasRenderingContext, private ActivityStateChangeObserver {
 public:
     static std::unique_ptr<WebGLRenderingContextBase> create(CanvasBase&, WebGLContextAttributes&, const String&);
     virtual ~WebGLRenderingContextBase();
@@ -330,10 +330,8 @@ public:
         SyntheticLostContext
     };
     void forceLostContext(LostContextMode);
-    void recycleContext();
     void forceRestoreContext();
     void loseContextImpl(LostContextMode);
-    void dispatchContextChangedEvent();
     WEBCORE_EXPORT void simulateContextChanged();
 
     GraphicsContext3D* graphicsContext3D() const { return m_context.get(); }
@@ -359,6 +357,12 @@ public:
     // Used for testing only, from Internals.
     WEBCORE_EXPORT void setFailNextGPUStatusCheck();
 
+    // GraphicsContext3D::Client
+    void didComposite() override;
+    void forceContextLost() override;
+    void recycleContext() override;
+    void dispatchContextChangedNotification() override;
+
 protected:
     WebGLRenderingContextBase(CanvasBase&, WebGLContextAttributes);
     WebGLRenderingContextBase(CanvasBase&, Ref<GraphicsContext3D>&&, WebGLContextAttributes);
index 0095342..a84edf3 100644 (file)
@@ -89,9 +89,8 @@ InspectorCanvas::InspectorCanvas(CanvasRenderingContext& context)
 
 HTMLCanvasElement* InspectorCanvas::canvasElement()
 {
-    auto* canvasBase = &m_context.canvasBase();
-    if (is<HTMLCanvasElement>(canvasBase))
-        return downcast<HTMLCanvasElement>(canvasBase);
+    if (is<HTMLCanvasElement>(m_context.canvasBase()))
+        return &downcast<HTMLCanvasElement>(m_context.canvasBase());
     return nullptr;
 }
 
@@ -137,6 +136,9 @@ static bool shouldSnapshotWebGLAction(const String& name)
 void InspectorCanvas::recordAction(const String& name, Vector<RecordCanvasActionVariant>&& parameters)
 {
     if (!m_initialState) {
+        // We should only construct the initial state for the first action of the recording.
+        ASSERT(!m_frames && !m_currentActions);
+
         m_initialState = buildInitialState();
         m_bufferUsed += m_initialState->memoryCost();
     }
@@ -171,26 +173,10 @@ void InspectorCanvas::recordAction(const String& name, Vector<RecordCanvasAction
 #endif
 }
 
-RefPtr<Inspector::Protocol::Recording::InitialState>&& InspectorCanvas::releaseInitialState()
-{
-    return WTFMove(m_initialState);
-}
-
-RefPtr<JSON::ArrayOf<Inspector::Protocol::Recording::Frame>>&& InspectorCanvas::releaseFrames()
+void InspectorCanvas::finalizeFrame()
 {
     appendActionSnapshotIfNeeded();
 
-    return WTFMove(m_frames);
-}
-
-RefPtr<JSON::ArrayOf<JSON::Value>>&& InspectorCanvas::releaseData()
-{
-    m_indexedDuplicateData.clear();
-    return WTFMove(m_serializedDuplicateData);
-}
-
-void InspectorCanvas::finalizeFrame()
-{
     if (m_frames && m_frames->length() && !std::isnan(m_currentFrameStartTime)) {
         auto currentFrame = static_cast<Inspector::Protocol::Recording::Frame*>(m_frames->get(m_frames->length() - 1).get());
         currentFrame->setDuration((MonotonicTime::now() - m_currentFrameStartTime).milliseconds());
@@ -311,22 +297,61 @@ Ref<Inspector::Protocol::Canvas::Canvas> InspectorCanvas::buildObjectForCanvas(b
     return canvas;
 }
 
-void InspectorCanvas::appendActionSnapshotIfNeeded()
+Ref<Inspector::Protocol::Recording::Recording> InspectorCanvas::releaseObjectForRecording()
 {
-    if (!m_actionNeedingSnapshot)
-        return;
+    ASSERT(!m_currentActions);
+    ASSERT(!m_actionNeedingSnapshot);
+    ASSERT(!m_frames);
 
-    m_actionNeedingSnapshot->addItem(indexForData(getCanvasContentAsDataURL()));
-    m_actionNeedingSnapshot = nullptr;
+    // FIXME: <https://webkit.org/b/176008> Web Inspector: Record actions performed on WebGL2RenderingContext
+
+    Inspector::Protocol::Recording::Type type;
+    if (is<CanvasRenderingContext2D>(m_context))
+        type = Inspector::Protocol::Recording::Type::Canvas2D;
+    else if (is<ImageBitmapRenderingContext>(m_context))
+        type = Inspector::Protocol::Recording::Type::CanvasBitmapRenderer;
+#if ENABLE(WEBGL)
+    else if (is<WebGLRenderingContext>(m_context))
+        type = Inspector::Protocol::Recording::Type::CanvasWebGL;
+#endif
+    else {
+        ASSERT_NOT_REACHED();
+        type = Inspector::Protocol::Recording::Type::Canvas2D;
+    }
+
+    auto recording = Inspector::Protocol::Recording::Recording::create()
+        .setVersion(Inspector::Protocol::Recording::VERSION)
+        .setType(type)
+        .setInitialState(m_initialState.releaseNonNull())
+        .setData(m_serializedDuplicateData.releaseNonNull())
+        .release();
+
+    if (!m_recordingName.isEmpty())
+        recording->setName(m_recordingName);
+
+    resetRecordingData();
+
+    return recording;
 }
 
-String InspectorCanvas::getCanvasContentAsDataURL()
+String InspectorCanvas::getCanvasContentAsDataURL(ErrorString& errorString)
 {
-    // FIXME: <https://webkit.org/b/180833> Web Inspector: support OffscreenCanvas for Canvas related operations
+    // FIXME: <https://webkit.org/b/173621> Web Inspector: Support getting the content of WebMetal context;
+    if (!is<CanvasRenderingContext2D>(m_context)
+#if ENABLE(WEBGL)
+        && !is<WebGLRenderingContextBase>(m_context)
+#endif
+        && !is<ImageBitmapRenderingContext>(m_context)) {
+        errorString = "Unsupported canvas context type"_s;
+        return emptyString();
+    }
 
+    // FIXME: <https://webkit.org/b/180833> Web Inspector: support OffscreenCanvas for Canvas related operations
     auto* node = canvasElement();
-    if (!node)
-        return String();
+    if (!node) {
+        errorString = "Context isn't related to an HTMLCanvasElement"_s;
+        return emptyString();
+    }
 
 #if ENABLE(WEBGL)
     if (is<WebGLRenderingContextBase>(m_context))
@@ -340,12 +365,29 @@ String InspectorCanvas::getCanvasContentAsDataURL()
         downcast<WebGLRenderingContextBase>(m_context).setPreventBufferClearForInspector(false);
 #endif
 
-    if (result.hasException())
-        return String();
+    if (result.hasException()) {
+        errorString = result.releaseException().releaseMessage();
+        return emptyString();
+    }
 
     return result.releaseReturnValue().string;
 }
 
+void InspectorCanvas::appendActionSnapshotIfNeeded()
+{
+    if (!m_actionNeedingSnapshot)
+        return;
+
+    m_bufferUsed -= m_actionNeedingSnapshot->memoryCost();
+
+    ErrorString ignored;
+    m_actionNeedingSnapshot->addItem(indexForData(getCanvasContentAsDataURL(ignored)));
+
+    m_bufferUsed += m_actionNeedingSnapshot->memoryCost();
+
+    m_actionNeedingSnapshot = nullptr;
+}
+
 int InspectorCanvas::indexForData(DuplicateDataVariant data)
 {
     size_t index = m_indexedDuplicateData.findMatching([&] (auto item) {
@@ -561,7 +603,8 @@ Ref<Inspector::Protocol::Recording::InitialState> InspectorCanvas::buildInitialS
     if (parametersPayload->length())
         initialStatePayload->setParameters(WTFMove(parametersPayload));
 
-    initialStatePayload->setContent(getCanvasContentAsDataURL());
+    ErrorString ignored;
+    initialStatePayload->setContent(getCanvasContentAsDataURL(ignored));
 
     return initialStatePayload;
 }
index b4b66d0..26c8671 100644 (file)
@@ -44,6 +44,8 @@ class HTMLVideoElement;
 class ImageBitmap;
 class ImageData;
 
+typedef String ErrorString;
+
 class InspectorCanvas final : public RefCounted<InspectorCanvas> {
 public:
     static Ref<InspectorCanvas> create(CanvasRenderingContext&);
@@ -58,14 +60,11 @@ public:
     bool currentFrameHasData() const;
     void recordAction(const String&, Vector<RecordCanvasActionVariant>&& = { });
 
-    RefPtr<Inspector::Protocol::Recording::InitialState>&& releaseInitialState();
-    RefPtr<JSON::ArrayOf<Inspector::Protocol::Recording::Frame>>&& releaseFrames();
-    RefPtr<JSON::ArrayOf<JSON::Value>>&& releaseData();
+    Ref<JSON::ArrayOf<Inspector::Protocol::Recording::Frame>> releaseFrames() { return m_frames.releaseNonNull(); }
 
     void finalizeFrame();
     void markCurrentFrameIncomplete();
 
-    const String& recordingName() const { return m_recordingName; }
     void setRecordingName(const String& name) { m_recordingName = name; }
 
     void setBufferLimit(long);
@@ -76,11 +75,13 @@ public:
     bool overFrameCount() const;
 
     Ref<Inspector::Protocol::Canvas::Canvas> buildObjectForCanvas(bool captureBacktrace);
+    Ref<Inspector::Protocol::Recording::Recording> releaseObjectForRecording();
+
+    String getCanvasContentAsDataURL(ErrorString&);
 
 private:
     InspectorCanvas(CanvasRenderingContext&);
     void appendActionSnapshotIfNeeded();
-    String getCanvasContentAsDataURL();
 
     using DuplicateDataVariant = Variant<
         RefPtr<CanvasGradient>,
index 941e249..d087b05 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "InspectorCanvasAgent.h"
 
+#include "ActiveDOMCallbackMicrotask.h"
 #include "CanvasRenderingContext.h"
 #include "CanvasRenderingContext2D.h"
 #include "Document.h"
@@ -37,6 +38,7 @@
 #include "JSCanvasRenderingContext2D.h"
 #include "JSExecState.h"
 #include "JSImageBitmapRenderingContext.h"
+#include "Microtasks.h"
 #include "OffscreenCanvas.h"
 #include "ScriptState.h"
 #include "StringAdaptors.h"
@@ -78,7 +80,6 @@ InspectorCanvasAgent::InspectorCanvasAgent(PageAgentContext& context)
     , m_injectedScriptManager(context.injectedScriptManager)
     , m_inspectedPage(context.inspectedPage)
     , m_canvasDestroyedTimer(*this, &InspectorCanvasAgent::canvasDestroyedTimerFired)
-    , m_canvasRecordingTimer(*this, &InspectorCanvasAgent::canvasRecordingTimerFired)
 {
 }
 
@@ -147,7 +148,7 @@ void InspectorCanvasAgent::disable(ErrorString&)
 
 void InspectorCanvasAgent::requestNode(ErrorString& errorString, const String& canvasId, int* nodeId)
 {
-    auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
+    auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
     if (!inspectorCanvas)
         return;
 
@@ -168,48 +169,16 @@ void InspectorCanvasAgent::requestNode(ErrorString& errorString, const String& c
 
 void InspectorCanvasAgent::requestContent(ErrorString& errorString, const String& canvasId, String* content)
 {
-    auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
+    auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
     if (!inspectorCanvas)
         return;
 
-    // FIXME: <https://webkit.org/b/180833> Web Inspector: support OffscreenCanvas for Canvas related operations
-
-    if (auto* node = inspectorCanvas->canvasElement()) {
-        if (is<CanvasRenderingContext2D>(inspectorCanvas->context()) || is<ImageBitmapRenderingContext>(inspectorCanvas->context())) {
-            auto result = node->toDataURL("image/png"_s);
-            if (result.hasException()) {
-                errorString = result.releaseException().releaseMessage();
-                return;
-            }
-            *content = result.releaseReturnValue().string;
-            return;
-        }
-
-#if ENABLE(WEBGL)
-        if (is<WebGLRenderingContextBase>(inspectorCanvas->context())) {
-            WebGLRenderingContextBase& contextWebGLBase = downcast<WebGLRenderingContextBase>(inspectorCanvas->context());
-
-            contextWebGLBase.setPreventBufferClearForInspector(true);
-            auto result = node->toDataURL("image/png"_s);
-            contextWebGLBase.setPreventBufferClearForInspector(false);
-
-            if (result.hasException()) {
-                errorString = result.releaseException().releaseMessage();
-                return;
-            }
-            *content = result.releaseReturnValue().string;
-            return;
-        }
-#endif
-    }
-
-    // FIXME: <https://webkit.org/b/173621> Web Inspector: Support getting the content of WebMetal context;
-    errorString = "Unsupported canvas context type"_s;
+    *content = inspectorCanvas->getCanvasContentAsDataURL(errorString);
 }
 
 void InspectorCanvasAgent::requestCSSCanvasClientNodes(ErrorString& errorString, const String& canvasId, RefPtr<JSON::ArrayOf<int>>& result)
 {
-    auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
+    auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
     if (!inspectorCanvas)
         return;
 
@@ -250,7 +219,7 @@ static JSC::JSValue contextAsScriptValue(JSC::ExecState& state, CanvasRenderingC
 
 void InspectorCanvasAgent::resolveCanvasContext(ErrorString& errorString, const String& canvasId, const String* objectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result)
 {
-    auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
+    auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
     if (!inspectorCanvas)
         return;
 
@@ -279,7 +248,7 @@ void InspectorCanvasAgent::setRecordingAutoCaptureFrameCount(ErrorString&, int c
 
 void InspectorCanvasAgent::startRecording(ErrorString& errorString, const String& canvasId, const int* frameCount, const int* memoryLimit)
 {
-    auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
+    auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
     if (!inspectorCanvas)
         return;
 
@@ -298,7 +267,7 @@ void InspectorCanvasAgent::startRecording(ErrorString& errorString, const String
 
 void InspectorCanvasAgent::stopRecording(ErrorString& errorString, const String& canvasId)
 {
-    auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
+    auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
     if (!inspectorCanvas)
         return;
 
@@ -313,7 +282,7 @@ void InspectorCanvasAgent::stopRecording(ErrorString& errorString, const String&
 void InspectorCanvasAgent::requestShaderSource(ErrorString& errorString, const String& programId, const String& shaderType, String* content)
 {
 #if ENABLE(WEBGL)
-    auto* inspectorProgram = assertInspectorProgram(errorString, programId);
+    auto inspectorProgram = assertInspectorProgram(errorString, programId);
     if (!inspectorProgram)
         return;
 
@@ -335,7 +304,7 @@ void InspectorCanvasAgent::requestShaderSource(ErrorString& errorString, const S
 void InspectorCanvasAgent::updateShader(ErrorString& errorString, const String& programId, const String& shaderType, const String& source)
 {
 #if ENABLE(WEBGL)
-    auto* inspectorProgram = assertInspectorProgram(errorString, programId);
+    auto inspectorProgram = assertInspectorProgram(errorString, programId);
     if (!inspectorProgram)
         return;
 
@@ -366,7 +335,7 @@ void InspectorCanvasAgent::updateShader(ErrorString& errorString, const String&
 void InspectorCanvasAgent::setShaderProgramDisabled(ErrorString& errorString, const String& programId, bool disabled)
 {
 #if ENABLE(WEBGL)
-    auto* inspectorProgram = assertInspectorProgram(errorString, programId);
+    auto inspectorProgram = assertInspectorProgram(errorString, programId);
     if (!inspectorProgram)
         return;
 
@@ -381,7 +350,7 @@ void InspectorCanvasAgent::setShaderProgramDisabled(ErrorString& errorString, co
 void InspectorCanvasAgent::setShaderProgramHighlighted(ErrorString& errorString, const String& programId, bool highlighted)
 {
 #if ENABLE(WEBGL)
-    auto* inspectorProgram = assertInspectorProgram(errorString, programId);
+    auto inspectorProgram = assertInspectorProgram(errorString, programId);
     if (!inspectorProgram)
         return;
 
@@ -424,7 +393,7 @@ void InspectorCanvasAgent::didChangeCSSCanvasClientNodes(CanvasBase& canvasBase)
         return;
     }
 
-    auto* inspectorCanvas = findInspectorCanvas(*context);
+    auto inspectorCanvas = findInspectorCanvas(*context);
     ASSERT(inspectorCanvas);
     if (!inspectorCanvas)
         return;
@@ -450,7 +419,7 @@ void InspectorCanvasAgent::didCreateCanvasRenderingContext(CanvasRenderingContex
 
 void InspectorCanvasAgent::didChangeCanvasMemory(CanvasRenderingContext& context)
 {
-    auto* inspectorCanvas = findInspectorCanvas(context);
+    auto inspectorCanvas = findInspectorCanvas(context);
     ASSERT(inspectorCanvas);
     if (!inspectorCanvas)
         return;
@@ -463,7 +432,7 @@ void InspectorCanvasAgent::didChangeCanvasMemory(CanvasRenderingContext& context
 
 void InspectorCanvasAgent::recordCanvasAction(CanvasRenderingContext& canvasRenderingContext, const String& name, Vector<RecordCanvasActionVariant>&& parameters)
 {
-    auto* inspectorCanvas = findInspectorCanvas(canvasRenderingContext);
+    auto inspectorCanvas = findInspectorCanvas(canvasRenderingContext);
     ASSERT(inspectorCanvas);
     if (!inspectorCanvas)
         return;
@@ -472,10 +441,24 @@ void InspectorCanvasAgent::recordCanvasAction(CanvasRenderingContext& canvasRend
     if (!canvasRenderingContext.callTracingActive())
         return;
 
-    inspectorCanvas->recordAction(name, WTFMove(parameters));
+    // Only enqueue a microtask for the first action of each frame. Any subsequent actions will be
+    // covered by the initial microtask until the next frame.
+    if (!inspectorCanvas->currentFrameHasData()) {
+        if (auto* scriptExecutionContext = inspectorCanvas->context().canvasBase().scriptExecutionContext()) {
+            auto& queue = MicrotaskQueue::mainThreadQueue();
+            queue.append(std::make_unique<ActiveDOMCallbackMicrotask>(queue, *scriptExecutionContext, [&, protectedInspectorCanvas = inspectorCanvas.copyRef()] {
+                if (auto* canvasElement = protectedInspectorCanvas->canvasElement()) {
+                    if (canvasElement->isDescendantOf(canvasElement->document()))
+                        return;
+                }
+
+                if (protectedInspectorCanvas->context().callTracingActive())
+                    didFinishRecordingCanvasFrame(protectedInspectorCanvas->context());
+            }));
+        }
+    }
 
-    if (!m_canvasRecordingTimer.isActive())
-        m_canvasRecordingTimer.startOneShot(0_s);
+    inspectorCanvas->recordAction(name, WTFMove(parameters));
 
     if (!inspectorCanvas->hasBufferSpace())
         didFinishRecordingCanvasFrame(inspectorCanvas->context(), true);
@@ -487,7 +470,8 @@ void InspectorCanvasAgent::canvasDestroyed(CanvasBase& canvasBase)
     if (!context)
         return;
 
-    auto* inspectorCanvas = findInspectorCanvas(*context);
+    auto inspectorCanvas = findInspectorCanvas(*context);
+    ASSERT(inspectorCanvas);
     if (!inspectorCanvas)
         return;
 
@@ -504,7 +488,7 @@ void InspectorCanvasAgent::canvasDestroyed(CanvasBase& canvasBase)
 
 void InspectorCanvasAgent::didFinishRecordingCanvasFrame(CanvasRenderingContext& context, bool forceDispatch)
 {
-    auto* inspectorCanvas = findInspectorCanvas(context);
+    auto inspectorCanvas = findInspectorCanvas(context);
     ASSERT(inspectorCanvas);
     if (!inspectorCanvas)
         return;
@@ -515,7 +499,6 @@ void InspectorCanvasAgent::didFinishRecordingCanvasFrame(CanvasRenderingContext&
     if (!inspectorCanvas->hasRecordingData()) {
         if (forceDispatch) {
             m_frontendDispatcher->recordingFinished(inspectorCanvas->identifier(), nullptr);
-
             inspectorCanvas->resetRecordingData();
         }
         return;
@@ -531,41 +514,12 @@ void InspectorCanvasAgent::didFinishRecordingCanvasFrame(CanvasRenderingContext&
     if (!forceDispatch && !inspectorCanvas->overFrameCount())
         return;
 
-    // FIXME: <https://webkit.org/b/176008> Web Inspector: Record actions performed on WebGL2RenderingContext
-
-    Inspector::Protocol::Recording::Type type;
-    if (is<CanvasRenderingContext2D>(inspectorCanvas->context()))
-        type = Inspector::Protocol::Recording::Type::Canvas2D;
-    else if (is<ImageBitmapRenderingContext>(inspectorCanvas->context()))
-        type = Inspector::Protocol::Recording::Type::CanvasBitmapRenderer;
-#if ENABLE(WEBGL)
-    else if (is<WebGLRenderingContext>(inspectorCanvas->context()))
-        type = Inspector::Protocol::Recording::Type::CanvasWebGL;
-#endif
-    else {
-        ASSERT_NOT_REACHED();
-        type = Inspector::Protocol::Recording::Type::Canvas2D;
-    }
-
-    auto recording = Inspector::Protocol::Recording::Recording::create()
-        .setVersion(Inspector::Protocol::Recording::VERSION)
-        .setType(type)
-        .setInitialState(inspectorCanvas->releaseInitialState())
-        .setData(inspectorCanvas->releaseData())
-        .release();
-
-    const String& name = inspectorCanvas->recordingName();
-    if (!name.isEmpty())
-        recording->setName(name);
-
-    m_frontendDispatcher->recordingFinished(inspectorCanvas->identifier(), WTFMove(recording));
-
-    inspectorCanvas->resetRecordingData();
+    m_frontendDispatcher->recordingFinished(inspectorCanvas->identifier(), inspectorCanvas->releaseObjectForRecording());
 }
 
 void InspectorCanvasAgent::consoleStartRecordingCanvas(CanvasRenderingContext& context, JSC::ExecState& exec, JSC::JSObject* options)
 {
-    auto* inspectorCanvas = findInspectorCanvas(context);
+    auto inspectorCanvas = findInspectorCanvas(context);
     ASSERT(inspectorCanvas);
     if (!inspectorCanvas)
         return;
@@ -587,7 +541,7 @@ void InspectorCanvasAgent::consoleStartRecordingCanvas(CanvasRenderingContext& c
 #if ENABLE(WEBGL)
 void InspectorCanvasAgent::didEnableExtension(WebGLRenderingContextBase& context, const String& extension)
 {
-    auto* inspectorCanvas = findInspectorCanvas(context);
+    auto inspectorCanvas = findInspectorCanvas(context);
     ASSERT(inspectorCanvas);
     if (!inspectorCanvas)
         return;
@@ -597,7 +551,7 @@ void InspectorCanvasAgent::didEnableExtension(WebGLRenderingContextBase& context
 
 void InspectorCanvasAgent::didCreateProgram(WebGLRenderingContextBase& context, WebGLProgram& program)
 {
-    auto* inspectorCanvas = findInspectorCanvas(context);
+    auto inspectorCanvas = findInspectorCanvas(context);
     ASSERT(inspectorCanvas);
     if (!inspectorCanvas)
         return;
@@ -610,7 +564,7 @@ void InspectorCanvasAgent::didCreateProgram(WebGLRenderingContextBase& context,
 
 void InspectorCanvasAgent::willDeleteProgram(WebGLProgram& program)
 {
-    auto* inspectorProgram = findInspectorProgram(program);
+    auto inspectorProgram = findInspectorProgram(program);
     if (!inspectorProgram)
         return;
 
@@ -620,7 +574,8 @@ void InspectorCanvasAgent::willDeleteProgram(WebGLProgram& program)
 
 bool InspectorCanvasAgent::isShaderProgramDisabled(WebGLProgram& program)
 {
-    auto* inspectorProgram = findInspectorProgram(program);
+    auto inspectorProgram = findInspectorProgram(program);
+    ASSERT(inspectorProgram);
     if (!inspectorProgram)
         return false;
 
@@ -629,7 +584,8 @@ bool InspectorCanvasAgent::isShaderProgramDisabled(WebGLProgram& program)
 
 bool InspectorCanvasAgent::isShaderProgramHighlighted(WebGLProgram& program)
 {
-    auto* inspectorProgram = findInspectorProgram(program);
+    auto inspectorProgram = findInspectorProgram(program);
+    ASSERT(inspectorProgram);
     if (!inspectorProgram)
         return false;
 
@@ -674,30 +630,17 @@ void InspectorCanvasAgent::canvasDestroyedTimerFired()
     m_removedCanvasIdentifiers.clear();
 }
 
-void InspectorCanvasAgent::canvasRecordingTimerFired()
-{
-    for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
-        if (!inspectorCanvas->context().callTracingActive())
-            continue;
-
-        didFinishRecordingCanvasFrame(inspectorCanvas->context());
-    }
-}
-
 void InspectorCanvasAgent::clearCanvasData()
 {
     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values())
         inspectorCanvas->context().canvasBase().removeObserver(*this);
 
     m_identifierToInspectorCanvas.clear();
-    m_removedCanvasIdentifiers.clear();
 #if ENABLE(WEBGL)
     m_identifierToInspectorProgram.clear();
+    m_removedCanvasIdentifiers.clear();
 #endif
 
-    if (m_canvasRecordingTimer.isActive())
-        m_canvasRecordingTimer.stop();
-
     if (m_canvasDestroyedTimer.isActive())
         m_canvasDestroyedTimer.stop();
 }
@@ -745,22 +688,22 @@ String InspectorCanvasAgent::unbindCanvas(InspectorCanvas& inspectorCanvas)
     return identifier;
 }
 
-InspectorCanvas* InspectorCanvasAgent::assertInspectorCanvas(ErrorString& errorString, const String& identifier)
+RefPtr<InspectorCanvas> InspectorCanvasAgent::assertInspectorCanvas(ErrorString& errorString, const String& identifier)
 {
-    RefPtr<InspectorCanvas> inspectorCanvas = m_identifierToInspectorCanvas.get(identifier);
+    auto inspectorCanvas = m_identifierToInspectorCanvas.get(identifier);
     if (!inspectorCanvas) {
         errorString = "No canvas for given identifier."_s;
         return nullptr;
     }
 
-    return inspectorCanvas.get();
+    return inspectorCanvas;
 }
 
-InspectorCanvas* InspectorCanvasAgent::findInspectorCanvas(CanvasRenderingContext& context)
+RefPtr<InspectorCanvas> InspectorCanvasAgent::findInspectorCanvas(CanvasRenderingContext& context)
 {
     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
         if (&inspectorCanvas->context() == &context)
-            return inspectorCanvas.get();
+            return inspectorCanvas;
     }
 
     return nullptr;
@@ -775,22 +718,22 @@ String InspectorCanvasAgent::unbindProgram(InspectorShaderProgram& inspectorProg
     return identifier;
 }
 
-InspectorShaderProgram* InspectorCanvasAgent::assertInspectorProgram(ErrorString& errorString, const String& identifier)
+RefPtr<InspectorShaderProgram> InspectorCanvasAgent::assertInspectorProgram(ErrorString& errorString, const String& identifier)
 {
-    RefPtr<InspectorShaderProgram> inspectorProgram = m_identifierToInspectorProgram.get(identifier);
+    auto inspectorProgram = m_identifierToInspectorProgram.get(identifier);
     if (!inspectorProgram) {
         errorString = "No shader program for given identifier."_s;
         return nullptr;
     }
 
-    return inspectorProgram.get();
+    return inspectorProgram;
 }
 
-InspectorShaderProgram* InspectorCanvasAgent::findInspectorProgram(WebGLProgram& program)
+RefPtr<InspectorShaderProgram> InspectorCanvasAgent::findInspectorProgram(WebGLProgram& program)
 {
     for (auto& inspectorProgram : m_identifierToInspectorProgram.values()) {
         if (&inspectorProgram->program() == &program)
-            return inspectorProgram.get();
+            return inspectorProgram;
     }
 
     return nullptr;
index 5b299c8..7bc8d87 100644 (file)
@@ -112,18 +112,15 @@ private:
     void startRecording(InspectorCanvas&, Inspector::Protocol::Recording::Initiator, RecordingOptions&& = { });
 
     void canvasDestroyedTimerFired();
-    void canvasRecordingTimerFired();
     void clearCanvasData();
     InspectorCanvas& bindCanvas(CanvasRenderingContext&, bool captureBacktrace);
     String unbindCanvas(InspectorCanvas&);
-    InspectorCanvas* assertInspectorCanvas(ErrorString&, const String& identifier);
-    InspectorCanvas* findInspectorCanvas(CanvasRenderingContext&);
+    RefPtr<InspectorCanvas> assertInspectorCanvas(ErrorString&, const String& identifier);
+    RefPtr<InspectorCanvas> findInspectorCanvas(CanvasRenderingContext&);
 #if ENABLE(WEBGL)
     String unbindProgram(InspectorShaderProgram&);
-    InspectorShaderProgram* assertInspectorProgram(ErrorString&, const String& identifier);
-    InspectorShaderProgram* findInspectorProgram(WebGLProgram&);
-
-    HashMap<String, RefPtr<InspectorShaderProgram>> m_identifierToInspectorProgram;
+    RefPtr<InspectorShaderProgram> assertInspectorProgram(ErrorString&, const String& identifier);
+    RefPtr<InspectorShaderProgram> findInspectorProgram(WebGLProgram&);
 #endif
 
     std::unique_ptr<Inspector::CanvasFrontendDispatcher> m_frontendDispatcher;
@@ -133,10 +130,14 @@ private:
     Page& m_inspectedPage;
 
     HashMap<String, RefPtr<InspectorCanvas>> m_identifierToInspectorCanvas;
+#if ENABLE(WEBGL)
+    HashMap<String, RefPtr<InspectorShaderProgram>> m_identifierToInspectorProgram;
+#endif
     Vector<String> m_removedCanvasIdentifiers;
+
     Optional<size_t> m_recordingAutoCaptureFrameCount;
+
     Timer m_canvasDestroyedTimer;
-    Timer m_canvasRecordingTimer;
 };
 
 } // namespace WebCore
index 65d264b..11b0e99 100644 (file)
@@ -120,6 +120,15 @@ class GraphicsContext3DPrivate;
 
 class GraphicsContext3D : public RefCounted<GraphicsContext3D> {
 public:
+    class Client {
+    public:
+        virtual ~Client() { }
+        virtual void didComposite() = 0;
+        virtual void forceContextLost() = 0;
+        virtual void recycleContext() = 0;
+        virtual void dispatchContextChangedNotification() = 0;
+    };
+
     enum {
         // WebGL 1 constants
         DEPTH_BUFFER_BIT = 0x00000100,
@@ -764,7 +773,9 @@ public:
 #endif
 
     bool makeContextCurrent();
-    void setWebGLContext(WebGLRenderingContextBase* base) { m_webglContext = base; }
+
+    void addClient(Client& client) { m_clients.add(&client); }
+    void removeClient(Client& client) { m_clients.remove(&client); }
 
     // With multisampling on, blit from multisampleFBO to regular FBO.
     void prepareTexture();
@@ -1507,8 +1518,7 @@ private:
     std::unique_ptr<GraphicsContext3DPrivate> m_private;
 #endif
 
-    // FIXME: Layering violation.
-    WebGLRenderingContextBase* m_webglContext { nullptr };
+    HashSet<Client*> m_clients;
 
     bool m_isForWebGL2 { false };
     bool m_usingCoreProfile { false };
index 546e741..c776b32 100644 (file)
@@ -2016,6 +2016,9 @@ void GraphicsContext3D::markContextChanged()
 void GraphicsContext3D::markLayerComposited()
 {
     m_layerComposited = true;
+
+    for (auto* client : m_clients)
+        client->didComposite();
 }
 
 bool GraphicsContext3D::layerComposited() const
@@ -2025,26 +2028,20 @@ bool GraphicsContext3D::layerComposited() const
 
 void GraphicsContext3D::forceContextLost()
 {
-#if ENABLE(WEBGL)
-    if (m_webglContext)
-        m_webglContext->forceLostContext(WebGLRenderingContextBase::RealLostContext);
-#endif
+    for (auto* client : m_clients)
+        client->forceContextLost();
 }
 
 void GraphicsContext3D::recycleContext()
 {
-#if ENABLE(WEBGL)
-    if (m_webglContext)
-        m_webglContext->recycleContext();
-#endif
+    for (auto* client : m_clients)
+        client->recycleContext();
 }
 
 void GraphicsContext3D::dispatchContextChangedNotification()
 {
-#if ENABLE(WEBGL)
-    if (m_webglContext)
-        m_webglContext->dispatchContextChangedEvent();
-#endif
+    for (auto* client : m_clients)
+        client->dispatchContextChangedNotification();
 }
 
 void GraphicsContext3D::texImage2DDirect(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dint border, GC3Denum format, GC3Denum type, const void* pixels)