<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[243356] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/243356">243356</a></dd>
<dt>Author</dt> <dd>drousso@apple.com</dd>
<dt>Date</dt> <dd>2019-03-21 19:47:35 -0700 (Thu, 21 Mar 2019)</dd>
</dl>

<h3>Log Message</h3>
<pre>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.

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:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsinspectorcanvasrecording2dhtml">trunk/LayoutTests/inspector/canvas/recording-2d.html</a></li>
<li><a href="#trunkLayoutTestsinspectorcanvasrecordingbitmaprendererhtml">trunk/LayoutTests/inspector/canvas/recording-bitmaprenderer.html</a></li>
<li><a href="#trunkLayoutTestsinspectorcanvasrecordinghtml2dhtml">trunk/LayoutTests/inspector/canvas/recording-html-2d.html</a></li>
<li><a href="#trunkLayoutTestsinspectorcanvasrecordingwebglhtml">trunk/LayoutTests/inspector/canvas/recording-webgl.html</a></li>
<li><a href="#trunkLayoutTestsinspectorcanvassetRecordingAutoCaptureFrameCounthtml">trunk/LayoutTests/inspector/canvas/setRecordingAutoCaptureFrameCount.html</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLCanvasElementcpp">trunk/Source/WebCore/html/HTMLCanvasElement.cpp</a></li>
<li><a href="#trunkSourceWebCorehtmlcanvasWebGLRenderingContextBasecpp">trunk/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp</a></li>
<li><a href="#trunkSourceWebCorehtmlcanvasWebGLRenderingContextBaseh">trunk/Source/WebCore/html/canvas/WebGLRenderingContextBase.h</a></li>
<li><a href="#trunkSourceWebCoreinspectorInspectorCanvascpp">trunk/Source/WebCore/inspector/InspectorCanvas.cpp</a></li>
<li><a href="#trunkSourceWebCoreinspectorInspectorCanvash">trunk/Source/WebCore/inspector/InspectorCanvas.h</a></li>
<li><a href="#trunkSourceWebCoreinspectoragentsInspectorCanvasAgentcpp">trunk/Source/WebCore/inspector/agents/InspectorCanvasAgent.cpp</a></li>
<li><a href="#trunkSourceWebCoreinspectoragentsInspectorCanvasAgenth">trunk/Source/WebCore/inspector/agents/InspectorCanvasAgent.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsGraphicsContext3Dh">trunk/Source/WebCore/platform/graphics/GraphicsContext3D.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsopenglGraphicsContext3DOpenGLCommoncpp">trunk/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (243355 => 243356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog      2019-03-22 02:33:38 UTC (rev 243355)
+++ trunk/LayoutTests/ChangeLog 2019-03-22 02:47:35 UTC (rev 243356)
</span><span class="lines">@@ -1,3 +1,17 @@
</span><ins>+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:
+
</ins><span class="cx"> 2019-03-21  Joseph Pecoraro  <pecoraro@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Web Inspector: Timelines - Cannot export on about:blank - suggested filename containing a colon silently fails
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorcanvasrecording2dhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/canvas/recording-2d.html (243355 => 243356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/canvas/recording-2d.html     2019-03-22 02:33:38 UTC (rev 243355)
+++ trunk/LayoutTests/inspector/canvas/recording-2d.html        2019-03-22 02:47:35 UTC (rev 243356)
</span><span class="lines">@@ -39,8 +39,6 @@
</span><span class="cx">     pattern = ctx.createPattern(image, "no-repeat");
</span><span class="cx">     bitmap = await createImageBitmap(image);
</span><span class="cx"> 
</span><del>-    document.body.appendChild(canvas);
-
</del><span class="cx">     ctx.save();
</span><span class="cx">     cancelActions();
</span><span class="cx"> 
</span><span class="lines">@@ -53,7 +51,7 @@
</span><span class="cx">     } catch (e) { }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-let timeoutID = NaN;
</del><ins>+let requestAnimationFrameId = NaN;
</ins><span class="cx"> let saveCount = 1;
</span><span class="cx"> 
</span><span class="cx"> function cancelActions() {
</span><span class="lines">@@ -61,8 +59,8 @@
</span><span class="cx">         ctx.restore();
</span><span class="cx">     ctx.restore(); // Ensures the state is reset between test cases.
</span><span class="cx"> 
</span><del>-    clearTimeout(timeoutID);
-    timeoutID = NaN;
</del><ins>+    cancelAnimationFrame(requestAnimationFrameId);
+    requestAnimationFrameId = NaN;
</ins><span class="cx"> 
</span><span class="cx">     ctx.save(); // Ensures the state is reset between test cases.
</span><span class="cx">     ctx.save(); // This matches the `restore` call in `performActions`.
</span><span class="lines">@@ -399,7 +397,7 @@
</span><span class="cx">     function executeFrameFunction() {
</span><span class="cx">         frames[index++]();
</span><span class="cx">         if (index < frames.length)
</span><del>-            timeoutID = setTimeout(executeFrameFunction, 0);
</del><ins>+            requestAnimationFrameId = requestAnimationFrame(executeFrameFunction);
</ins><span class="cx">     };
</span><span class="cx">     executeFrameFunction();
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorcanvasrecordingbitmaprendererhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/canvas/recording-bitmaprenderer.html (243355 => 243356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/canvas/recording-bitmaprenderer.html 2019-03-22 02:33:38 UTC (rev 243355)
+++ trunk/LayoutTests/inspector/canvas/recording-bitmaprenderer.html    2019-03-22 02:47:35 UTC (rev 243356)
</span><span class="lines">@@ -23,7 +23,7 @@
</span><span class="cx"> function load() {
</span><span class="cx">     ctx = canvas.getContext("bitmaprenderer");
</span><span class="cx"> 
</span><del>-    document.body.appendChild(canvas);
</del><ins>+    cancelActions();
</ins><span class="cx"> 
</span><span class="cx">     runTest();
</span><span class="cx"> }
</span><span class="lines">@@ -34,11 +34,11 @@
</span><span class="cx">     } catch (e) { }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-let timeoutID = NaN;
</del><ins>+let requestAnimationFrameId = NaN;
</ins><span class="cx"> 
</span><span class="cx"> function cancelActions() {
</span><del>-    clearTimeout(timeoutID);
-    timeoutID = NaN;
</del><ins>+    cancelAnimationFrame(requestAnimationFrameId);
+    requestAnimationFrameId = NaN;
</ins><span class="cx"> 
</span><span class="cx">     createImageBitmap(transparentImage).then((transparentBitmap) => {
</span><span class="cx">         ctx.transferFromImageBitmap(transparentBitmap);
</span><span class="lines">@@ -68,7 +68,7 @@
</span><span class="cx">     function executeFrameFunction() {
</span><span class="cx">         frames[index++]();
</span><span class="cx">         if (index < frames.length)
</span><del>-            timeoutID = setTimeout(executeFrameFunction, 0);
</del><ins>+            requestAnimationFrameId = requestAnimationFrame(executeFrameFunction);
</ins><span class="cx">     };
</span><span class="cx">     executeFrameFunction();
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorcanvasrecordinghtml2dhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/canvas/recording-html-2d.html (243355 => 243356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/canvas/recording-html-2d.html        2019-03-22 02:33:38 UTC (rev 243355)
+++ trunk/LayoutTests/inspector/canvas/recording-html-2d.html   2019-03-22 02:47:35 UTC (rev 243356)
</span><span class="lines">@@ -39,7 +39,7 @@
</span><span class="cx"> 
</span><span class="cx">     imageBitmap = await createImageBitmap(image);
</span><span class="cx"> 
</span><del>-    document.body.appendChild(canvas);
</del><ins>+    cancelActions();
</ins><span class="cx"> 
</span><span class="cx">     context.strokeStyle = "red";
</span><span class="cx">     context.save();
</span><span class="lines">@@ -56,7 +56,15 @@
</span><span class="cx">     } catch (e) { }
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+let requestAnimationFrameId = NaN;
+
</ins><span class="cx"> function cancelActions() {
</span><ins>+    cancelAnimationFrame(requestAnimationFrameId);
+    requestAnimationFrameId = NaN;
+
+    context.resetTransform();
+    context.beginPath();
+    context.clearRect(0, 0, context.canvas.width, context.canvas.height);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> function performActions() {
</span><span class="lines">@@ -97,7 +105,7 @@
</span><span class="cx">     function executeFrameFunction() {
</span><span class="cx">         frames[index++]();
</span><span class="cx">         if (index < frames.length)
</span><del>-            timeoutID = setTimeout(executeFrameFunction, 0);
</del><ins>+            requestAnimationFrameId = requestAnimationFrame(executeFrameFunction);
</ins><span class="cx">     };
</span><span class="cx">     executeFrameFunction();
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorcanvasrecordingwebglhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/canvas/recording-webgl.html (243355 => 243356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/canvas/recording-webgl.html  2019-03-22 02:33:38 UTC (rev 243355)
+++ trunk/LayoutTests/inspector/canvas/recording-webgl.html     2019-03-22 02:47:35 UTC (rev 243356)
</span><span class="lines">@@ -49,7 +49,7 @@
</span><span class="cx">     shader = context.createShader(context.VERTEX_SHADER);
</span><span class="cx">     texture = context.createTexture();
</span><span class="cx"> 
</span><del>-    document.body.appendChild(context.canvas);
</del><ins>+    cancelActions();
</ins><span class="cx"> 
</span><span class="cx">     runTest();
</span><span class="cx"> }
</span><span class="lines">@@ -60,11 +60,11 @@
</span><span class="cx">     } catch (e) { }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-let timeoutID = NaN;
</del><ins>+let requestAnimationFrameId = NaN;
</ins><span class="cx"> 
</span><span class="cx"> function cancelActions() {
</span><del>-    clearTimeout(timeoutID);
-    timeoutID = NaN;
</del><ins>+    cancelAnimationFrame(requestAnimationFrameId);
+    requestAnimationFrameId = NaN;
</ins><span class="cx"> 
</span><span class="cx">     context.clearColor(0.0, 0.0, 0.0, 0.0);
</span><span class="cx">     context.clear(context.COLOR_BUFFER_BIT);
</span><span class="lines">@@ -502,7 +502,7 @@
</span><span class="cx">     function executeFrameFunction() {
</span><span class="cx">         frames[index++]();
</span><span class="cx">         if (index < frames.length)
</span><del>-            timeoutID = setTimeout(executeFrameFunction, 0);
</del><ins>+            requestAnimationFrameId = requestAnimationFrame(executeFrameFunction);
</ins><span class="cx">     };
</span><span class="cx">     executeFrameFunction();
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorcanvassetRecordingAutoCaptureFrameCounthtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/canvas/setRecordingAutoCaptureFrameCount.html (243355 => 243356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/canvas/setRecordingAutoCaptureFrameCount.html        2019-03-22 02:33:38 UTC (rev 243355)
+++ trunk/LayoutTests/inspector/canvas/setRecordingAutoCaptureFrameCount.html   2019-03-22 02:47:35 UTC (rev 243356)
</span><span class="lines">@@ -6,11 +6,11 @@
</span><span class="cx"> if (window.internals)
</span><span class="cx">     window.internals.settings.setWebGLErrorsToConsoleEnabled(false);
</span><span class="cx"> 
</span><del>-let timeoutID = NaN;
</del><ins>+let requestAnimationFrameId = NaN;
</ins><span class="cx"> 
</span><span class="cx"> function cancelActions() {
</span><del>-    clearTimeout(timeoutID);
-    timeoutID = NaN;
</del><ins>+    cancelAnimationFrame(requestAnimationFrameId);
+    requestAnimationFrameId = NaN;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> function performActions(frames) {
</span><span class="lines">@@ -19,7 +19,7 @@
</span><span class="cx">         frames[index++]();
</span><span class="cx"> 
</span><span class="cx">         if (index < frames.length)
</span><del>-            timeoutID = setTimeout(executeFrameFunction, 0);
</del><ins>+            requestAnimationFrameId = requestAnimationFrame(executeFrameFunction);
</ins><span class="cx">         else {
</span><span class="cx">             setTimeout(() => {
</span><span class="cx">                 TestPage.dispatchEventToFrontend("LastFrame");
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (243355 => 243356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2019-03-22 02:33:38 UTC (rev 243355)
+++ trunk/Source/WebCore/ChangeLog      2019-03-22 02:47:35 UTC (rev 243356)
</span><span class="lines">@@ -1,3 +1,106 @@
</span><ins>+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`.
+
</ins><span class="cx"> 2019-03-21  Tim Horton  <timothy_horton@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Adopt UIWKDocumentContext
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLCanvasElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLCanvasElement.cpp (243355 => 243356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLCanvasElement.cpp  2019-03-22 02:33:38 UTC (rev 243355)
+++ trunk/Source/WebCore/html/HTMLCanvasElement.cpp     2019-03-22 02:47:35 UTC (rev 243356)
</span><span class="lines">@@ -668,38 +668,40 @@
</span><span class="cx"> 
</span><span class="cx"> void HTMLCanvasElement::paint(GraphicsContext& context, const LayoutRect& r)
</span><span class="cx"> {
</span><del>-    if (UNLIKELY(m_context && m_context->callTracingActive()))
-        InspectorInstrumentation::didFinishRecordingCanvasFrame(*m_context);
-
</del><span class="cx">     // Clear the dirty rect
</span><span class="cx">     m_dirtyRect = FloatRect();
</span><span class="cx"> 
</span><del>-    if (context.paintingDisabled())
-        return;
-    
-    if (m_context) {
-        if (!paintsIntoCanvasBuffer() && !document().printing())
-            return;
</del><ins>+    if (!context.paintingDisabled()) {
+        bool shouldPaint = true;
</ins><span class="cx"> 
</span><del>-        m_context->paintRenderingResultsToCanvas();
-    }
</del><ins>+        if (m_context) {
+            shouldPaint = paintsIntoCanvasBuffer() || document().printing();
+            if (shouldPaint)
+                m_context->paintRenderingResultsToCanvas();
+        }
</ins><span class="cx"> 
</span><del>-    if (hasCreatedImageBuffer()) {
-        ImageBuffer* imageBuffer = buffer();
-        if (imageBuffer) {
-            if (m_presentedImage) {
-                ImageOrientationDescription orientationDescription;
</del><ins>+        if (shouldPaint) {
+            if (hasCreatedImageBuffer()) {
+                ImageBuffer* imageBuffer = buffer();
+                if (imageBuffer) {
+                    if (m_presentedImage) {
+                        ImageOrientationDescription orientationDescription;
</ins><span class="cx"> #if ENABLE(CSS_IMAGE_ORIENTATION)
</span><del>-                orientationDescription.setImageOrientationEnum(renderer()->style().imageOrientation());
-#endif 
-                context.drawImage(*m_presentedImage, snappedIntRect(r), ImagePaintingOptions(orientationDescription));
-            } else
-                context.drawImageBuffer(*imageBuffer, snappedIntRect(r));
</del><ins>+                        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();
</ins><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (isGPUBased())
-        downcast<GPUBasedCanvasRenderingContext>(*m_context).markLayerComposited();
</del><ins>+    if (UNLIKELY(m_context && m_context->callTracingActive()))
+        InspectorInstrumentation::didFinishRecordingCanvasFrame(*m_context);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool HTMLCanvasElement::isGPUBased() const
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlcanvasWebGLRenderingContextBasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp (243355 => 243356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp   2019-03-22 02:33:38 UTC (rev 243355)
+++ trunk/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp      2019-03-22 02:47:35 UTC (rev 243356)
</span><span class="lines">@@ -661,7 +661,7 @@
</span><span class="cx">     m_contextGroup = WebGLContextGroup::create();
</span><span class="cx">     m_contextGroup->addContext(*this);
</span><span class="cx">     
</span><del>-    m_context->setWebGLContext(this);
</del><ins>+    m_context->addClient(*this);
</ins><span class="cx"> 
</span><span class="cx">     m_context->getIntegerv(GraphicsContext3D::MAX_VIEWPORT_DIMS, m_maxViewportDims);
</span><span class="cx"> 
</span><span class="lines">@@ -914,6 +914,7 @@
</span><span class="cx">     removeActivityStateChangeObserver();
</span><span class="cx"> 
</span><span class="cx">     if (m_context) {
</span><ins>+        m_context->removeClient(*this);
</ins><span class="cx">         m_context->setContextLostCallback(nullptr);
</span><span class="cx">         m_context->setErrorMessageCallback(nullptr);
</span><span class="cx">         m_context = nullptr;
</span><span class="lines">@@ -5042,15 +5043,6 @@
</span><span class="cx">     m_contextGroup->loseContextGroup(mode);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-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();
-}
-
</del><span class="cx"> void WebGLRenderingContextBase::loseContextImpl(WebGLRenderingContextBase::LostContextMode mode)
</span><span class="cx"> {
</span><span class="cx">     if (isContextLost())
</span><span class="lines">@@ -6225,15 +6217,6 @@
</span><span class="cx">     canvas->dispatchEvent(WebGLContextEvent::create(eventNames().webglcontextrestoredEvent, Event::CanBubble::No, Event::IsCancelable::Yes, emptyString()));
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void WebGLRenderingContextBase::dispatchContextChangedEvent()
-{
-    auto* canvas = htmlCanvas();
-    if (!canvas)
-        return;
-
-    canvas->dispatchEvent(WebGLContextEvent::create(eventNames().webglcontextchangedEvent, Event::CanBubble::No, Event::IsCancelable::Yes, emptyString()));
-}
-
</del><span class="cx"> void WebGLRenderingContextBase::simulateContextChanged()
</span><span class="cx"> {
</span><span class="cx">     if (m_context)
</span><span class="lines">@@ -6513,6 +6496,36 @@
</span><span class="cx">     m_context->setFailNextGPUStatusCheck();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+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()));
+}
+
+
</ins><span class="cx"> } // namespace WebCore
</span><span class="cx"> 
</span><span class="cx"> #endif // ENABLE(WEBGL)
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlcanvasWebGLRenderingContextBaseh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/canvas/WebGLRenderingContextBase.h (243355 => 243356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/canvas/WebGLRenderingContextBase.h     2019-03-22 02:33:38 UTC (rev 243355)
+++ trunk/Source/WebCore/html/canvas/WebGLRenderingContextBase.h        2019-03-22 02:47:35 UTC (rev 243356)
</span><span class="lines">@@ -94,7 +94,7 @@
</span><span class="cx"> 
</span><span class="cx"> using WebGLCanvas = WTF::Variant<RefPtr<HTMLCanvasElement>, RefPtr<OffscreenCanvas>>;
</span><span class="cx"> 
</span><del>-class WebGLRenderingContextBase : public GPUBasedCanvasRenderingContext, private ActivityStateChangeObserver {
</del><ins>+class WebGLRenderingContextBase : public GraphicsContext3D::Client, public GPUBasedCanvasRenderingContext, private ActivityStateChangeObserver {
</ins><span class="cx"> public:
</span><span class="cx">     static std::unique_ptr<WebGLRenderingContextBase> create(CanvasBase&, WebGLContextAttributes&, const String&);
</span><span class="cx">     virtual ~WebGLRenderingContextBase();
</span><span class="lines">@@ -330,10 +330,8 @@
</span><span class="cx">         SyntheticLostContext
</span><span class="cx">     };
</span><span class="cx">     void forceLostContext(LostContextMode);
</span><del>-    void recycleContext();
</del><span class="cx">     void forceRestoreContext();
</span><span class="cx">     void loseContextImpl(LostContextMode);
</span><del>-    void dispatchContextChangedEvent();
</del><span class="cx">     WEBCORE_EXPORT void simulateContextChanged();
</span><span class="cx"> 
</span><span class="cx">     GraphicsContext3D* graphicsContext3D() const { return m_context.get(); }
</span><span class="lines">@@ -359,6 +357,12 @@
</span><span class="cx">     // Used for testing only, from Internals.
</span><span class="cx">     WEBCORE_EXPORT void setFailNextGPUStatusCheck();
</span><span class="cx"> 
</span><ins>+    // GraphicsContext3D::Client
+    void didComposite() override;
+    void forceContextLost() override;
+    void recycleContext() override;
+    void dispatchContextChangedNotification() override;
+
</ins><span class="cx"> protected:
</span><span class="cx">     WebGLRenderingContextBase(CanvasBase&, WebGLContextAttributes);
</span><span class="cx">     WebGLRenderingContextBase(CanvasBase&, Ref<GraphicsContext3D>&&, WebGLContextAttributes);
</span></span></pre></div>
<a id="trunkSourceWebCoreinspectorInspectorCanvascpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/inspector/InspectorCanvas.cpp (243355 => 243356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/inspector/InspectorCanvas.cpp       2019-03-22 02:33:38 UTC (rev 243355)
+++ trunk/Source/WebCore/inspector/InspectorCanvas.cpp  2019-03-22 02:47:35 UTC (rev 243356)
</span><span class="lines">@@ -89,9 +89,8 @@
</span><span class="cx"> 
</span><span class="cx"> HTMLCanvasElement* InspectorCanvas::canvasElement()
</span><span class="cx"> {
</span><del>-    auto* canvasBase = &m_context.canvasBase();
-    if (is<HTMLCanvasElement>(canvasBase))
-        return downcast<HTMLCanvasElement>(canvasBase);
</del><ins>+    if (is<HTMLCanvasElement>(m_context.canvasBase()))
+        return &downcast<HTMLCanvasElement>(m_context.canvasBase());
</ins><span class="cx">     return nullptr;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -137,6 +136,9 @@
</span><span class="cx"> void InspectorCanvas::recordAction(const String& name, Vector<RecordCanvasActionVariant>&& parameters)
</span><span class="cx"> {
</span><span class="cx">     if (!m_initialState) {
</span><ins>+        // We should only construct the initial state for the first action of the recording.
+        ASSERT(!m_frames && !m_currentActions);
+
</ins><span class="cx">         m_initialState = buildInitialState();
</span><span class="cx">         m_bufferUsed += m_initialState->memoryCost();
</span><span class="cx">     }
</span><span class="lines">@@ -171,26 +173,10 @@
</span><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-RefPtr<Inspector::Protocol::Recording::InitialState>&& InspectorCanvas::releaseInitialState()
</del><ins>+void InspectorCanvas::finalizeFrame()
</ins><span class="cx"> {
</span><del>-    return WTFMove(m_initialState);
-}
-
-RefPtr<JSON::ArrayOf<Inspector::Protocol::Recording::Frame>>&& InspectorCanvas::releaseFrames()
-{
</del><span class="cx">     appendActionSnapshotIfNeeded();
</span><span class="cx"> 
</span><del>-    return WTFMove(m_frames);
-}
-
-RefPtr<JSON::ArrayOf<JSON::Value>>&& InspectorCanvas::releaseData()
-{
-    m_indexedDuplicateData.clear();
-    return WTFMove(m_serializedDuplicateData);
-}
-
-void InspectorCanvas::finalizeFrame()
-{
</del><span class="cx">     if (m_frames && m_frames->length() && !std::isnan(m_currentFrameStartTime)) {
</span><span class="cx">         auto currentFrame = static_cast<Inspector::Protocol::Recording::Frame*>(m_frames->get(m_frames->length() - 1).get());
</span><span class="cx">         currentFrame->setDuration((MonotonicTime::now() - m_currentFrameStartTime).milliseconds());
</span><span class="lines">@@ -311,22 +297,61 @@
</span><span class="cx">     return canvas;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void InspectorCanvas::appendActionSnapshotIfNeeded()
</del><ins>+Ref<Inspector::Protocol::Recording::Recording> InspectorCanvas::releaseObjectForRecording()
</ins><span class="cx"> {
</span><del>-    if (!m_actionNeedingSnapshot)
-        return;
</del><ins>+    ASSERT(!m_currentActions);
+    ASSERT(!m_actionNeedingSnapshot);
+    ASSERT(!m_frames);
</ins><span class="cx"> 
</span><del>-    m_actionNeedingSnapshot->addItem(indexForData(getCanvasContentAsDataURL()));
-    m_actionNeedingSnapshot = nullptr;
</del><ins>+    // 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;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-String InspectorCanvas::getCanvasContentAsDataURL()
</del><ins>+String InspectorCanvas::getCanvasContentAsDataURL(ErrorString& errorString)
</ins><span class="cx"> {
</span><ins>+    // 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();
+    }
+
</ins><span class="cx">     // FIXME: <https://webkit.org/b/180833> Web Inspector: support OffscreenCanvas for Canvas related operations
</span><del>-
</del><span class="cx">     auto* node = canvasElement();
</span><del>-    if (!node)
-        return String();
</del><ins>+    if (!node) {
+        errorString = "Context isn't related to an HTMLCanvasElement"_s;
+        return emptyString();
+    }
</ins><span class="cx"> 
</span><span class="cx"> #if ENABLE(WEBGL)
</span><span class="cx">     if (is<WebGLRenderingContextBase>(m_context))
</span><span class="lines">@@ -340,12 +365,29 @@
</span><span class="cx">         downcast<WebGLRenderingContextBase>(m_context).setPreventBufferClearForInspector(false);
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-    if (result.hasException())
-        return String();
</del><ins>+    if (result.hasException()) {
+        errorString = result.releaseException().releaseMessage();
+        return emptyString();
+    }
</ins><span class="cx"> 
</span><span class="cx">     return result.releaseReturnValue().string;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+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;
+}
+
</ins><span class="cx"> int InspectorCanvas::indexForData(DuplicateDataVariant data)
</span><span class="cx"> {
</span><span class="cx">     size_t index = m_indexedDuplicateData.findMatching([&] (auto item) {
</span><span class="lines">@@ -561,7 +603,8 @@
</span><span class="cx">     if (parametersPayload->length())
</span><span class="cx">         initialStatePayload->setParameters(WTFMove(parametersPayload));
</span><span class="cx"> 
</span><del>-    initialStatePayload->setContent(getCanvasContentAsDataURL());
</del><ins>+    ErrorString ignored;
+    initialStatePayload->setContent(getCanvasContentAsDataURL(ignored));
</ins><span class="cx"> 
</span><span class="cx">     return initialStatePayload;
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoreinspectorInspectorCanvash"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/inspector/InspectorCanvas.h (243355 => 243356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/inspector/InspectorCanvas.h 2019-03-22 02:33:38 UTC (rev 243355)
+++ trunk/Source/WebCore/inspector/InspectorCanvas.h    2019-03-22 02:47:35 UTC (rev 243356)
</span><span class="lines">@@ -44,6 +44,8 @@
</span><span class="cx"> class ImageBitmap;
</span><span class="cx"> class ImageData;
</span><span class="cx"> 
</span><ins>+typedef String ErrorString;
+
</ins><span class="cx"> class InspectorCanvas final : public RefCounted<InspectorCanvas> {
</span><span class="cx"> public:
</span><span class="cx">     static Ref<InspectorCanvas> create(CanvasRenderingContext&);
</span><span class="lines">@@ -58,14 +60,11 @@
</span><span class="cx">     bool currentFrameHasData() const;
</span><span class="cx">     void recordAction(const String&, Vector<RecordCanvasActionVariant>&& = { });
</span><span class="cx"> 
</span><del>-    RefPtr<Inspector::Protocol::Recording::InitialState>&& releaseInitialState();
-    RefPtr<JSON::ArrayOf<Inspector::Protocol::Recording::Frame>>&& releaseFrames();
-    RefPtr<JSON::ArrayOf<JSON::Value>>&& releaseData();
</del><ins>+    Ref<JSON::ArrayOf<Inspector::Protocol::Recording::Frame>> releaseFrames() { return m_frames.releaseNonNull(); }
</ins><span class="cx"> 
</span><span class="cx">     void finalizeFrame();
</span><span class="cx">     void markCurrentFrameIncomplete();
</span><span class="cx"> 
</span><del>-    const String& recordingName() const { return m_recordingName; }
</del><span class="cx">     void setRecordingName(const String& name) { m_recordingName = name; }
</span><span class="cx"> 
</span><span class="cx">     void setBufferLimit(long);
</span><span class="lines">@@ -76,11 +75,13 @@
</span><span class="cx">     bool overFrameCount() const;
</span><span class="cx"> 
</span><span class="cx">     Ref<Inspector::Protocol::Canvas::Canvas> buildObjectForCanvas(bool captureBacktrace);
</span><ins>+    Ref<Inspector::Protocol::Recording::Recording> releaseObjectForRecording();
</ins><span class="cx"> 
</span><ins>+    String getCanvasContentAsDataURL(ErrorString&);
+
</ins><span class="cx"> private:
</span><span class="cx">     InspectorCanvas(CanvasRenderingContext&);
</span><span class="cx">     void appendActionSnapshotIfNeeded();
</span><del>-    String getCanvasContentAsDataURL();
</del><span class="cx"> 
</span><span class="cx">     using DuplicateDataVariant = Variant<
</span><span class="cx">         RefPtr<CanvasGradient>,
</span></span></pre></div>
<a id="trunkSourceWebCoreinspectoragentsInspectorCanvasAgentcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/inspector/agents/InspectorCanvasAgent.cpp (243355 => 243356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/inspector/agents/InspectorCanvasAgent.cpp   2019-03-22 02:33:38 UTC (rev 243355)
+++ trunk/Source/WebCore/inspector/agents/InspectorCanvasAgent.cpp      2019-03-22 02:47:35 UTC (rev 243356)
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> #include "config.h"
</span><span class="cx"> #include "InspectorCanvasAgent.h"
</span><span class="cx"> 
</span><ins>+#include "ActiveDOMCallbackMicrotask.h"
</ins><span class="cx"> #include "CanvasRenderingContext.h"
</span><span class="cx"> #include "CanvasRenderingContext2D.h"
</span><span class="cx"> #include "Document.h"
</span><span class="lines">@@ -37,6 +38,7 @@
</span><span class="cx"> #include "JSCanvasRenderingContext2D.h"
</span><span class="cx"> #include "JSExecState.h"
</span><span class="cx"> #include "JSImageBitmapRenderingContext.h"
</span><ins>+#include "Microtasks.h"
</ins><span class="cx"> #include "OffscreenCanvas.h"
</span><span class="cx"> #include "ScriptState.h"
</span><span class="cx"> #include "StringAdaptors.h"
</span><span class="lines">@@ -78,7 +80,6 @@
</span><span class="cx">     , m_injectedScriptManager(context.injectedScriptManager)
</span><span class="cx">     , m_inspectedPage(context.inspectedPage)
</span><span class="cx">     , m_canvasDestroyedTimer(*this, &InspectorCanvasAgent::canvasDestroyedTimerFired)
</span><del>-    , m_canvasRecordingTimer(*this, &InspectorCanvasAgent::canvasRecordingTimerFired)
</del><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -147,7 +148,7 @@
</span><span class="cx"> 
</span><span class="cx"> void InspectorCanvasAgent::requestNode(ErrorString& errorString, const String& canvasId, int* nodeId)
</span><span class="cx"> {
</span><del>-    auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
</del><ins>+    auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
</ins><span class="cx">     if (!inspectorCanvas)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="lines">@@ -168,48 +169,16 @@
</span><span class="cx"> 
</span><span class="cx"> void InspectorCanvasAgent::requestContent(ErrorString& errorString, const String& canvasId, String* content)
</span><span class="cx"> {
</span><del>-    auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
</del><ins>+    auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
</ins><span class="cx">     if (!inspectorCanvas)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    // 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;
</del><ins>+    *content = inspectorCanvas->getCanvasContentAsDataURL(errorString);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void InspectorCanvasAgent::requestCSSCanvasClientNodes(ErrorString& errorString, const String& canvasId, RefPtr<JSON::ArrayOf<int>>& result)
</span><span class="cx"> {
</span><del>-    auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
</del><ins>+    auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
</ins><span class="cx">     if (!inspectorCanvas)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="lines">@@ -250,7 +219,7 @@
</span><span class="cx"> 
</span><span class="cx"> void InspectorCanvasAgent::resolveCanvasContext(ErrorString& errorString, const String& canvasId, const String* objectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result)
</span><span class="cx"> {
</span><del>-    auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
</del><ins>+    auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
</ins><span class="cx">     if (!inspectorCanvas)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="lines">@@ -279,7 +248,7 @@
</span><span class="cx"> 
</span><span class="cx"> void InspectorCanvasAgent::startRecording(ErrorString& errorString, const String& canvasId, const int* frameCount, const int* memoryLimit)
</span><span class="cx"> {
</span><del>-    auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
</del><ins>+    auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
</ins><span class="cx">     if (!inspectorCanvas)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="lines">@@ -298,7 +267,7 @@
</span><span class="cx"> 
</span><span class="cx"> void InspectorCanvasAgent::stopRecording(ErrorString& errorString, const String& canvasId)
</span><span class="cx"> {
</span><del>-    auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
</del><ins>+    auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
</ins><span class="cx">     if (!inspectorCanvas)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="lines">@@ -313,7 +282,7 @@
</span><span class="cx"> void InspectorCanvasAgent::requestShaderSource(ErrorString& errorString, const String& programId, const String& shaderType, String* content)
</span><span class="cx"> {
</span><span class="cx"> #if ENABLE(WEBGL)
</span><del>-    auto* inspectorProgram = assertInspectorProgram(errorString, programId);
</del><ins>+    auto inspectorProgram = assertInspectorProgram(errorString, programId);
</ins><span class="cx">     if (!inspectorProgram)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="lines">@@ -335,7 +304,7 @@
</span><span class="cx"> void InspectorCanvasAgent::updateShader(ErrorString& errorString, const String& programId, const String& shaderType, const String& source)
</span><span class="cx"> {
</span><span class="cx"> #if ENABLE(WEBGL)
</span><del>-    auto* inspectorProgram = assertInspectorProgram(errorString, programId);
</del><ins>+    auto inspectorProgram = assertInspectorProgram(errorString, programId);
</ins><span class="cx">     if (!inspectorProgram)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="lines">@@ -366,7 +335,7 @@
</span><span class="cx"> void InspectorCanvasAgent::setShaderProgramDisabled(ErrorString& errorString, const String& programId, bool disabled)
</span><span class="cx"> {
</span><span class="cx"> #if ENABLE(WEBGL)
</span><del>-    auto* inspectorProgram = assertInspectorProgram(errorString, programId);
</del><ins>+    auto inspectorProgram = assertInspectorProgram(errorString, programId);
</ins><span class="cx">     if (!inspectorProgram)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="lines">@@ -381,7 +350,7 @@
</span><span class="cx"> void InspectorCanvasAgent::setShaderProgramHighlighted(ErrorString& errorString, const String& programId, bool highlighted)
</span><span class="cx"> {
</span><span class="cx"> #if ENABLE(WEBGL)
</span><del>-    auto* inspectorProgram = assertInspectorProgram(errorString, programId);
</del><ins>+    auto inspectorProgram = assertInspectorProgram(errorString, programId);
</ins><span class="cx">     if (!inspectorProgram)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="lines">@@ -424,7 +393,7 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    auto* inspectorCanvas = findInspectorCanvas(*context);
</del><ins>+    auto inspectorCanvas = findInspectorCanvas(*context);
</ins><span class="cx">     ASSERT(inspectorCanvas);
</span><span class="cx">     if (!inspectorCanvas)
</span><span class="cx">         return;
</span><span class="lines">@@ -450,7 +419,7 @@
</span><span class="cx"> 
</span><span class="cx"> void InspectorCanvasAgent::didChangeCanvasMemory(CanvasRenderingContext& context)
</span><span class="cx"> {
</span><del>-    auto* inspectorCanvas = findInspectorCanvas(context);
</del><ins>+    auto inspectorCanvas = findInspectorCanvas(context);
</ins><span class="cx">     ASSERT(inspectorCanvas);
</span><span class="cx">     if (!inspectorCanvas)
</span><span class="cx">         return;
</span><span class="lines">@@ -463,7 +432,7 @@
</span><span class="cx"> 
</span><span class="cx"> void InspectorCanvasAgent::recordCanvasAction(CanvasRenderingContext& canvasRenderingContext, const String& name, Vector<RecordCanvasActionVariant>&& parameters)
</span><span class="cx"> {
</span><del>-    auto* inspectorCanvas = findInspectorCanvas(canvasRenderingContext);
</del><ins>+    auto inspectorCanvas = findInspectorCanvas(canvasRenderingContext);
</ins><span class="cx">     ASSERT(inspectorCanvas);
</span><span class="cx">     if (!inspectorCanvas)
</span><span class="cx">         return;
</span><span class="lines">@@ -472,11 +441,25 @@
</span><span class="cx">     if (!canvasRenderingContext.callTracingActive())
</span><span class="cx">         return;
</span><span class="cx"> 
</span><ins>+    // 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());
+            }));
+        }
+    }
+
</ins><span class="cx">     inspectorCanvas->recordAction(name, WTFMove(parameters));
</span><span class="cx"> 
</span><del>-    if (!m_canvasRecordingTimer.isActive())
-        m_canvasRecordingTimer.startOneShot(0_s);
-
</del><span class="cx">     if (!inspectorCanvas->hasBufferSpace())
</span><span class="cx">         didFinishRecordingCanvasFrame(inspectorCanvas->context(), true);
</span><span class="cx"> }
</span><span class="lines">@@ -487,7 +470,8 @@
</span><span class="cx">     if (!context)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    auto* inspectorCanvas = findInspectorCanvas(*context);
</del><ins>+    auto inspectorCanvas = findInspectorCanvas(*context);
+    ASSERT(inspectorCanvas);
</ins><span class="cx">     if (!inspectorCanvas)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="lines">@@ -504,7 +488,7 @@
</span><span class="cx"> 
</span><span class="cx"> void InspectorCanvasAgent::didFinishRecordingCanvasFrame(CanvasRenderingContext& context, bool forceDispatch)
</span><span class="cx"> {
</span><del>-    auto* inspectorCanvas = findInspectorCanvas(context);
</del><ins>+    auto inspectorCanvas = findInspectorCanvas(context);
</ins><span class="cx">     ASSERT(inspectorCanvas);
</span><span class="cx">     if (!inspectorCanvas)
</span><span class="cx">         return;
</span><span class="lines">@@ -515,7 +499,6 @@
</span><span class="cx">     if (!inspectorCanvas->hasRecordingData()) {
</span><span class="cx">         if (forceDispatch) {
</span><span class="cx">             m_frontendDispatcher->recordingFinished(inspectorCanvas->identifier(), nullptr);
</span><del>-
</del><span class="cx">             inspectorCanvas->resetRecordingData();
</span><span class="cx">         }
</span><span class="cx">         return;
</span><span class="lines">@@ -531,41 +514,12 @@
</span><span class="cx">     if (!forceDispatch && !inspectorCanvas->overFrameCount())
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    // 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();
</del><ins>+    m_frontendDispatcher->recordingFinished(inspectorCanvas->identifier(), inspectorCanvas->releaseObjectForRecording());
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void InspectorCanvasAgent::consoleStartRecordingCanvas(CanvasRenderingContext& context, JSC::ExecState& exec, JSC::JSObject* options)
</span><span class="cx"> {
</span><del>-    auto* inspectorCanvas = findInspectorCanvas(context);
</del><ins>+    auto inspectorCanvas = findInspectorCanvas(context);
</ins><span class="cx">     ASSERT(inspectorCanvas);
</span><span class="cx">     if (!inspectorCanvas)
</span><span class="cx">         return;
</span><span class="lines">@@ -587,7 +541,7 @@
</span><span class="cx"> #if ENABLE(WEBGL)
</span><span class="cx"> void InspectorCanvasAgent::didEnableExtension(WebGLRenderingContextBase& context, const String& extension)
</span><span class="cx"> {
</span><del>-    auto* inspectorCanvas = findInspectorCanvas(context);
</del><ins>+    auto inspectorCanvas = findInspectorCanvas(context);
</ins><span class="cx">     ASSERT(inspectorCanvas);
</span><span class="cx">     if (!inspectorCanvas)
</span><span class="cx">         return;
</span><span class="lines">@@ -597,7 +551,7 @@
</span><span class="cx"> 
</span><span class="cx"> void InspectorCanvasAgent::didCreateProgram(WebGLRenderingContextBase& context, WebGLProgram& program)
</span><span class="cx"> {
</span><del>-    auto* inspectorCanvas = findInspectorCanvas(context);
</del><ins>+    auto inspectorCanvas = findInspectorCanvas(context);
</ins><span class="cx">     ASSERT(inspectorCanvas);
</span><span class="cx">     if (!inspectorCanvas)
</span><span class="cx">         return;
</span><span class="lines">@@ -610,7 +564,7 @@
</span><span class="cx"> 
</span><span class="cx"> void InspectorCanvasAgent::willDeleteProgram(WebGLProgram& program)
</span><span class="cx"> {
</span><del>-    auto* inspectorProgram = findInspectorProgram(program);
</del><ins>+    auto inspectorProgram = findInspectorProgram(program);
</ins><span class="cx">     if (!inspectorProgram)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="lines">@@ -620,7 +574,8 @@
</span><span class="cx"> 
</span><span class="cx"> bool InspectorCanvasAgent::isShaderProgramDisabled(WebGLProgram& program)
</span><span class="cx"> {
</span><del>-    auto* inspectorProgram = findInspectorProgram(program);
</del><ins>+    auto inspectorProgram = findInspectorProgram(program);
+    ASSERT(inspectorProgram);
</ins><span class="cx">     if (!inspectorProgram)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><span class="lines">@@ -629,7 +584,8 @@
</span><span class="cx"> 
</span><span class="cx"> bool InspectorCanvasAgent::isShaderProgramHighlighted(WebGLProgram& program)
</span><span class="cx"> {
</span><del>-    auto* inspectorProgram = findInspectorProgram(program);
</del><ins>+    auto inspectorProgram = findInspectorProgram(program);
+    ASSERT(inspectorProgram);
</ins><span class="cx">     if (!inspectorProgram)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><span class="lines">@@ -674,16 +630,6 @@
</span><span class="cx">     m_removedCanvasIdentifiers.clear();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void InspectorCanvasAgent::canvasRecordingTimerFired()
-{
-    for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
-        if (!inspectorCanvas->context().callTracingActive())
-            continue;
-
-        didFinishRecordingCanvasFrame(inspectorCanvas->context());
-    }
-}
-
</del><span class="cx"> void InspectorCanvasAgent::clearCanvasData()
</span><span class="cx"> {
</span><span class="cx">     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values())
</span><span class="lines">@@ -690,14 +636,11 @@
</span><span class="cx">         inspectorCanvas->context().canvasBase().removeObserver(*this);
</span><span class="cx"> 
</span><span class="cx">     m_identifierToInspectorCanvas.clear();
</span><del>-    m_removedCanvasIdentifiers.clear();
</del><span class="cx"> #if ENABLE(WEBGL)
</span><span class="cx">     m_identifierToInspectorProgram.clear();
</span><ins>+    m_removedCanvasIdentifiers.clear();
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-    if (m_canvasRecordingTimer.isActive())
-        m_canvasRecordingTimer.stop();
-
</del><span class="cx">     if (m_canvasDestroyedTimer.isActive())
</span><span class="cx">         m_canvasDestroyedTimer.stop();
</span><span class="cx"> }
</span><span class="lines">@@ -745,22 +688,22 @@
</span><span class="cx">     return identifier;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-InspectorCanvas* InspectorCanvasAgent::assertInspectorCanvas(ErrorString& errorString, const String& identifier)
</del><ins>+RefPtr<InspectorCanvas> InspectorCanvasAgent::assertInspectorCanvas(ErrorString& errorString, const String& identifier)
</ins><span class="cx"> {
</span><del>-    RefPtr<InspectorCanvas> inspectorCanvas = m_identifierToInspectorCanvas.get(identifier);
</del><ins>+    auto inspectorCanvas = m_identifierToInspectorCanvas.get(identifier);
</ins><span class="cx">     if (!inspectorCanvas) {
</span><span class="cx">         errorString = "No canvas for given identifier."_s;
</span><span class="cx">         return nullptr;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    return inspectorCanvas.get();
</del><ins>+    return inspectorCanvas;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-InspectorCanvas* InspectorCanvasAgent::findInspectorCanvas(CanvasRenderingContext& context)
</del><ins>+RefPtr<InspectorCanvas> InspectorCanvasAgent::findInspectorCanvas(CanvasRenderingContext& context)
</ins><span class="cx"> {
</span><span class="cx">     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
</span><span class="cx">         if (&inspectorCanvas->context() == &context)
</span><del>-            return inspectorCanvas.get();
</del><ins>+            return inspectorCanvas;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     return nullptr;
</span><span class="lines">@@ -775,22 +718,22 @@
</span><span class="cx">     return identifier;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-InspectorShaderProgram* InspectorCanvasAgent::assertInspectorProgram(ErrorString& errorString, const String& identifier)
</del><ins>+RefPtr<InspectorShaderProgram> InspectorCanvasAgent::assertInspectorProgram(ErrorString& errorString, const String& identifier)
</ins><span class="cx"> {
</span><del>-    RefPtr<InspectorShaderProgram> inspectorProgram = m_identifierToInspectorProgram.get(identifier);
</del><ins>+    auto inspectorProgram = m_identifierToInspectorProgram.get(identifier);
</ins><span class="cx">     if (!inspectorProgram) {
</span><span class="cx">         errorString = "No shader program for given identifier."_s;
</span><span class="cx">         return nullptr;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    return inspectorProgram.get();
</del><ins>+    return inspectorProgram;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-InspectorShaderProgram* InspectorCanvasAgent::findInspectorProgram(WebGLProgram& program)
</del><ins>+RefPtr<InspectorShaderProgram> InspectorCanvasAgent::findInspectorProgram(WebGLProgram& program)
</ins><span class="cx"> {
</span><span class="cx">     for (auto& inspectorProgram : m_identifierToInspectorProgram.values()) {
</span><span class="cx">         if (&inspectorProgram->program() == &program)
</span><del>-            return inspectorProgram.get();
</del><ins>+            return inspectorProgram;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     return nullptr;
</span></span></pre></div>
<a id="trunkSourceWebCoreinspectoragentsInspectorCanvasAgenth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/inspector/agents/InspectorCanvasAgent.h (243355 => 243356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/inspector/agents/InspectorCanvasAgent.h     2019-03-22 02:33:38 UTC (rev 243355)
+++ trunk/Source/WebCore/inspector/agents/InspectorCanvasAgent.h        2019-03-22 02:47:35 UTC (rev 243356)
</span><span class="lines">@@ -112,18 +112,15 @@
</span><span class="cx">     void startRecording(InspectorCanvas&, Inspector::Protocol::Recording::Initiator, RecordingOptions&& = { });
</span><span class="cx"> 
</span><span class="cx">     void canvasDestroyedTimerFired();
</span><del>-    void canvasRecordingTimerFired();
</del><span class="cx">     void clearCanvasData();
</span><span class="cx">     InspectorCanvas& bindCanvas(CanvasRenderingContext&, bool captureBacktrace);
</span><span class="cx">     String unbindCanvas(InspectorCanvas&);
</span><del>-    InspectorCanvas* assertInspectorCanvas(ErrorString&, const String& identifier);
-    InspectorCanvas* findInspectorCanvas(CanvasRenderingContext&);
</del><ins>+    RefPtr<InspectorCanvas> assertInspectorCanvas(ErrorString&, const String& identifier);
+    RefPtr<InspectorCanvas> findInspectorCanvas(CanvasRenderingContext&);
</ins><span class="cx"> #if ENABLE(WEBGL)
</span><span class="cx">     String unbindProgram(InspectorShaderProgram&);
</span><del>-    InspectorShaderProgram* assertInspectorProgram(ErrorString&, const String& identifier);
-    InspectorShaderProgram* findInspectorProgram(WebGLProgram&);
-
-    HashMap<String, RefPtr<InspectorShaderProgram>> m_identifierToInspectorProgram;
</del><ins>+    RefPtr<InspectorShaderProgram> assertInspectorProgram(ErrorString&, const String& identifier);
+    RefPtr<InspectorShaderProgram> findInspectorProgram(WebGLProgram&);
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     std::unique_ptr<Inspector::CanvasFrontendDispatcher> m_frontendDispatcher;
</span><span class="lines">@@ -133,10 +130,14 @@
</span><span class="cx">     Page& m_inspectedPage;
</span><span class="cx"> 
</span><span class="cx">     HashMap<String, RefPtr<InspectorCanvas>> m_identifierToInspectorCanvas;
</span><ins>+#if ENABLE(WEBGL)
+    HashMap<String, RefPtr<InspectorShaderProgram>> m_identifierToInspectorProgram;
+#endif
</ins><span class="cx">     Vector<String> m_removedCanvasIdentifiers;
</span><ins>+
</ins><span class="cx">     Optional<size_t> m_recordingAutoCaptureFrameCount;
</span><ins>+
</ins><span class="cx">     Timer m_canvasDestroyedTimer;
</span><del>-    Timer m_canvasRecordingTimer;
</del><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsGraphicsContext3Dh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/GraphicsContext3D.h (243355 => 243356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/GraphicsContext3D.h       2019-03-22 02:33:38 UTC (rev 243355)
+++ trunk/Source/WebCore/platform/graphics/GraphicsContext3D.h  2019-03-22 02:47:35 UTC (rev 243356)
</span><span class="lines">@@ -120,6 +120,15 @@
</span><span class="cx"> 
</span><span class="cx"> class GraphicsContext3D : public RefCounted<GraphicsContext3D> {
</span><span class="cx"> public:
</span><ins>+    class Client {
+    public:
+        virtual ~Client() { }
+        virtual void didComposite() = 0;
+        virtual void forceContextLost() = 0;
+        virtual void recycleContext() = 0;
+        virtual void dispatchContextChangedNotification() = 0;
+    };
+
</ins><span class="cx">     enum {
</span><span class="cx">         // WebGL 1 constants
</span><span class="cx">         DEPTH_BUFFER_BIT = 0x00000100,
</span><span class="lines">@@ -764,8 +773,10 @@
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     bool makeContextCurrent();
</span><del>-    void setWebGLContext(WebGLRenderingContextBase* base) { m_webglContext = base; }
</del><span class="cx"> 
</span><ins>+    void addClient(Client& client) { m_clients.add(&client); }
+    void removeClient(Client& client) { m_clients.remove(&client); }
+
</ins><span class="cx">     // With multisampling on, blit from multisampleFBO to regular FBO.
</span><span class="cx">     void prepareTexture();
</span><span class="cx"> 
</span><span class="lines">@@ -1507,8 +1518,7 @@
</span><span class="cx">     std::unique_ptr<GraphicsContext3DPrivate> m_private;
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-    // FIXME: Layering violation.
-    WebGLRenderingContextBase* m_webglContext { nullptr };
</del><ins>+    HashSet<Client*> m_clients;
</ins><span class="cx"> 
</span><span class="cx">     bool m_isForWebGL2 { false };
</span><span class="cx">     bool m_usingCoreProfile { false };
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsopenglGraphicsContext3DOpenGLCommoncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp (243355 => 243356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp  2019-03-22 02:33:38 UTC (rev 243355)
+++ trunk/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp     2019-03-22 02:47:35 UTC (rev 243356)
</span><span class="lines">@@ -2016,6 +2016,9 @@
</span><span class="cx"> void GraphicsContext3D::markLayerComposited()
</span><span class="cx"> {
</span><span class="cx">     m_layerComposited = true;
</span><ins>+
+    for (auto* client : m_clients)
+        client->didComposite();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool GraphicsContext3D::layerComposited() const
</span><span class="lines">@@ -2025,26 +2028,20 @@
</span><span class="cx"> 
</span><span class="cx"> void GraphicsContext3D::forceContextLost()
</span><span class="cx"> {
</span><del>-#if ENABLE(WEBGL)
-    if (m_webglContext)
-        m_webglContext->forceLostContext(WebGLRenderingContextBase::RealLostContext);
-#endif
</del><ins>+    for (auto* client : m_clients)
+        client->forceContextLost();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void GraphicsContext3D::recycleContext()
</span><span class="cx"> {
</span><del>-#if ENABLE(WEBGL)
-    if (m_webglContext)
-        m_webglContext->recycleContext();
-#endif
</del><ins>+    for (auto* client : m_clients)
+        client->recycleContext();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void GraphicsContext3D::dispatchContextChangedNotification()
</span><span class="cx"> {
</span><del>-#if ENABLE(WEBGL)
-    if (m_webglContext)
-        m_webglContext->dispatchContextChangedEvent();
-#endif
</del><ins>+    for (auto* client : m_clients)
+        client->dispatchContextChangedNotification();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void GraphicsContext3D::texImage2DDirect(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dint border, GC3Denum format, GC3Denum type, const void* pixels)
</span></span></pre>
</div>
</div>

</body>
</html>