Web Inspector: Canvas: instrument WebGPUDevice instead of GPUCanvasContext
authordrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 11 Sep 2019 23:28:37 +0000 (23:28 +0000)
committerdrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 11 Sep 2019 23:28:37 +0000 (23:28 +0000)
https://bugs.webkit.org/show_bug.cgi?id=201650

Reviewed by Joseph Pecoraro.

Source/JavaScriptCore:

Most of the actual "work" done with Web GPU actually uses a `WebGPUDevice`.

A `GPUCanvasContext` is basically just a display "client" of the device, and isn't even
required (e.g. compute pipeline).  We should treat the `GPUCanvasContext` almost like a
`-webkit-canvas` client of a `WebGPUDevice`.

* inspector/protocol/Canvas.json:
 - Add `powerPreference` key to `ContextAttributes` type.
 - Rename `requestCSSCanvasClientNodes` command to `requestClientNodes` for the above reason.
 - Rename `cssCanvasClientNodesChanged` event to `clientNodesChanged` for the above reason.
 - Rename `resolveCanvasContext` command to `resolveContext` since a `WebGPUDevice` isn't
   really a "canvas".

Source/WebCore:

Most of the actual "work" done with Web GPU actually uses a `WebGPUDevice`.

A `GPUCanvasContext` is basically just a display "client" of the device, and isn't even
required (e.g. compute pipeline).  We should treat the `GPUCanvasContext` almost like a
`-webkit-canvas` client of a `WebGPUDevice`.

Tests: inspector/canvas/create-context-webgpu.html
       inspector/canvas/requestClientNodes-webgpu.html
       inspector/canvas/resolveContext-webgpu.html

* Modules/webgpu/WebGPUAdapter.cpp:
(WebCore::WebGPUAdapter::requestDevice const):
Notify web inspector after a device is created.

* Modules/webgpu/WebGPUDevice.idl:
* Modules/webgpu/WebGPUDevice.h:
* Modules/webgpu/WebGPUDevice.cpp:
(WebCore::WebGPUDevice::instances): Added.
(WebCore::WebGPUDevice::instancesMutex): Added.
(WebCore::WebGPUDevice::~WebGPUDevice): Added.
Notify web inspector when the device is about to be destructed.

* Modules/webgpu/GPUCanvasContext.h:
* Modules/webgpu/GPUCanvasContext.cpp:
(WebCore::GPUCanvasContext::create):
(WebCore::GPUCanvasContext::configureSwapChain):

* inspector/InspectorCanvas.h:
* inspector/InspectorCanvas.cpp:
(WebCore::canvasIfContextMatchesDevice): Added.
(WebCore::InspectorCanvas::create):
(WebCore::InspectorCanvas::InspectorCanvas):
(WebCore::InspectorCanvas::canvasContext const): Added.
(WebCore::InspectorCanvas::canvasElement const): Added.
(WebCore::InspectorCanvas::isDeviceForCanvasContext const): Added.
(WebCore::InspectorCanvas::deviceContext const): Added.
(WebCore::InspectorCanvas::scriptExecutionContext const): Added.
(WebCore::InspectorCanvas::resolveContext const): Added.
(WebCore::InspectorCanvas::clientNodes const): Added.
(WebCore::InspectorCanvas::canvasChanged):
(WebCore::InspectorCanvas::resetRecordingData):
(WebCore::InspectorCanvas::recordAction):
(WebCore::InspectorCanvas::buildObjectForCanvas):
(WebCore::InspectorCanvas::releaseObjectForRecording):
(WebCore::InspectorCanvas::getCanvasContentAsDataURL):
(WebCore::InspectorCanvas::buildInitialState):
(WebCore::InspectorCanvas::canvasElement): Deleted.
* inspector/InspectorInstrumentation.cpp:
(WebCore::InspectorInstrumentation::didCreateWebGPUDeviceImpl): Added.
(WebCore::InspectorInstrumentation::willDestroyWebGPUDeviceImpl): Added.
(WebCore::InspectorInstrumentation::willConfigureSwapChainImpl): Added.
* inspector/InspectorInstrumentation.h:
(WebCore::InspectorInstrumentation::didCreateWebGPUDevice): Added.
(WebCore::InspectorInstrumentation::willDestroyWebGPUDevice): Added.
(WebCore::InspectorInstrumentation::willConfigureSwapChain): Added.

* inspector/agents/InspectorCanvasAgent.h:
* inspector/agents/InspectorCanvasAgent.cpp:
(WebCore::InspectorCanvasAgent::enable):
(WebCore::InspectorCanvasAgent::requestClientNodes): Added.
(WebCore::InspectorCanvasAgent::resolveContext): Added.
(WebCore::InspectorCanvasAgent::startRecording):
(WebCore::InspectorCanvasAgent::stopRecording):
(WebCore::InspectorCanvasAgent::frameNavigated):
(WebCore::InspectorCanvasAgent::didChangeCSSCanvasClientNodes):
(WebCore::InspectorCanvasAgent::didChangeCanvasMemory):
(WebCore::InspectorCanvasAgent::canvasDestroyed):
(WebCore::InspectorCanvasAgent::recordCanvasAction):
(WebCore::InspectorCanvasAgent::didFinishRecordingCanvasFrame):
(WebCore::InspectorCanvasAgent::didCreateWebGPUDevice): Added.
(WebCore::InspectorCanvasAgent::willDestroyWebGPUDevice): Added.
(WebCore::InspectorCanvasAgent::willConfigureSwapChain): Added.
(WebCore::InspectorCanvasAgent::clearCanvasData):
(WebCore::InspectorCanvasAgent::bindCanvas):
(WebCore::InspectorCanvasAgent::unbindCanvas):
(WebCore::InspectorCanvasAgent::findInspectorCanvas):
(WebCore::InspectorCanvasAgent::requestCSSCanvasClientNodes): Deleted.
(WebCore::contextAsScriptValue): Deleted.
(WebCore::InspectorCanvasAgent::resolveCanvasContext): Deleted.

* inspector/InspectorShaderProgram.cpp:
(WebCore::InspectorShaderProgram::context const):

Source/WebInspectorUI:

Most of the actual "work" done with Web GPU actually uses a `WebGPUDevice`.

A `GPUCanvasContext` is basically just a display "client" of the device, and isn't even
required (e.g. compute pipeline).  We should treat the `GPUCanvasContext` almost like a
`-webkit-canvas` client of a `WebGPUDevice`.

* UserInterface/Protocol/CanvasObserver.js:
(WI.CanvasObserver.prototype.clientNodesChanged): Added.
(WI.CanvasObserver.prototype.cssCanvasClientNodesChanged):
* UserInterface/Controllers/CanvasManager.js:
(WI.CanvasManager.prototype.clientNodesChanged): Added.
(WI.CanvasManager.prototype.cssCanvasClientNodesChanged): Deleted.
* UserInterface/Models/Canvas.js:
(WI.Canvas.resetUniqueDisplayNameNumbers):
(WI.Canvas.prototype.get displayName):
(WI.Canvas.prototype.requestNode):
(WI.Canvas.prototype.requestClientNodes): Added.
(WI.Canvas.prototype.requestSize):
(WI.Canvas.prototype.clientNodesChanged): Added.
(WI.Canvas.prototype.requestCSSCanvasClientNodes): Deleted.
(WI.Canvas.prototype.cssCanvasClientNodesChanged): Deleted.

* UserInterface/Protocol/RemoteObject.js:
(WI.RemoteObject.resolveCanvasContext):

* UserInterface/Views/CanvasContentView.js:
(WI.CanvasContentView.prototype.attached):
(WI.CanvasContentView.prototype._refreshPixelSize):
* UserInterface/Views/CanvasDetailsSidebarPanel.js:
(WI.CanvasDetailsSidebarPanel.prototype.set canvas):
(WI.CanvasDetailsSidebarPanel.prototype.initialLayout):
(WI.CanvasDetailsSidebarPanel.prototype.layout):
(WI.CanvasDetailsSidebarPanel.prototype._refreshSourceSection):
(WI.CanvasDetailsSidebarPanel.prototype._refreshClientsSection): Added.
(WI.CanvasDetailsSidebarPanel.prototype._refreshCSSCanvasSection): Deleted.
* UserInterface/Views/CanvasOverviewContentView.js:
(WI.CanvasOverviewContentView.prototype._contentViewMouseEnter):
* UserInterface/Views/CanvasTreeElement.js:
(WI.CanvasTreeElement.prototype._handleMouseOver):

* Localizations/en.lproj/localizedStrings.js:

LayoutTests:

* inspector/canvas/create-context-webgpu.html:
* inspector/canvas/create-context-webgpu-expected.txt:
* inspector/canvas/resources/create-context-utilities.js:
(createDetachedCanvas):
(createCSSCanvas):
(destroyCanvases):

* inspector/canvas/requestClientNodes.html: Added.
* inspector/canvas/requestClientNodes-expected.txt: Added.
* inspector/canvas/requestClientNodes-css.html: Renamed from LayoutTests/inspector/canvas/css-canvas-clients.html.
* inspector/canvas/requestClientNodes-css-expected.txt: Renamed from LayoutTests/inspector/canvas/css-canvas-clients-expected.txt.
* inspector/canvas/requestClientNodes-webgpu.html: Added.
* inspector/canvas/requestClientNodes-webgpu-expected.txt: Added.

* inspector/canvas/resolveContext-2d.html: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-2d.html.
* inspector/canvas/resolveContext-2d-expected.txt: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-2d-expected.txt.
* inspector/canvas/resolveContext-bitmaprenderer.html: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-bitmaprenderer.html.
* inspector/canvas/resolveContext-bitmaprenderer-expected.txt: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-bitmaprenderer-expected.txt.
* inspector/canvas/resolveContext-webgl.html: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-webgl.html.
* inspector/canvas/resolveContext-webgl-expected.txt: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-webgl-expected.txt.
* inspector/canvas/resolveContext-webgl2.html: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-webgl2.html.
* inspector/canvas/resolveContext-webgl2-expected.txt: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-webgl2-expected.txt.
* inspector/canvas/resolveContext-webgpu.html: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-webgpu.html.
* inspector/canvas/resolveContext-webgpu-expected.txt: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-webgpu-expected.txt.

* inspector/canvas/context-attributes-expected.txt:

* platform/gtk/TestExpectations:
* platform/ios/TestExpectations:
* platform/mac-wk1/TestExpectations:
* platform/mac/TestExpectations:
* platform/win/TestExpectations:
* platform/wincairo/TestExpectations:
* platform/wpe/TestExpectations:

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

62 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/canvas/context-attributes-expected.txt
LayoutTests/inspector/canvas/create-context-webgpu-expected.txt
LayoutTests/inspector/canvas/create-context-webgpu.html
LayoutTests/inspector/canvas/css-canvas-clients-expected.txt [deleted file]
LayoutTests/inspector/canvas/css-canvas-clients.html [deleted file]
LayoutTests/inspector/canvas/requestClientNodes-css-expected.txt [new file with mode: 0644]
LayoutTests/inspector/canvas/requestClientNodes-css.html [new file with mode: 0644]
LayoutTests/inspector/canvas/requestClientNodes-expected.txt [new file with mode: 0644]
LayoutTests/inspector/canvas/requestClientNodes-webgpu-expected.txt [new file with mode: 0644]
LayoutTests/inspector/canvas/requestClientNodes-webgpu.html [new file with mode: 0644]
LayoutTests/inspector/canvas/requestClientNodes.html [new file with mode: 0644]
LayoutTests/inspector/canvas/resolveCanvasContext-2d-expected.txt [deleted file]
LayoutTests/inspector/canvas/resolveCanvasContext-bitmaprenderer-expected.txt [deleted file]
LayoutTests/inspector/canvas/resolveCanvasContext-webgl-expected.txt [deleted file]
LayoutTests/inspector/canvas/resolveCanvasContext-webgl2-expected.txt [deleted file]
LayoutTests/inspector/canvas/resolveCanvasContext-webgpu-expected.txt [deleted file]
LayoutTests/inspector/canvas/resolveCanvasContext-webgpu.html [deleted file]
LayoutTests/inspector/canvas/resolveContext-2d-expected.txt [new file with mode: 0644]
LayoutTests/inspector/canvas/resolveContext-2d.html [moved from LayoutTests/inspector/canvas/resolveCanvasContext-2d.html with 77% similarity]
LayoutTests/inspector/canvas/resolveContext-bitmaprenderer-expected.txt [new file with mode: 0644]
LayoutTests/inspector/canvas/resolveContext-bitmaprenderer.html [moved from LayoutTests/inspector/canvas/resolveCanvasContext-bitmaprenderer.html with 77% similarity]
LayoutTests/inspector/canvas/resolveContext-webgl-expected.txt [new file with mode: 0644]
LayoutTests/inspector/canvas/resolveContext-webgl.html [moved from LayoutTests/inspector/canvas/resolveCanvasContext-webgl.html with 78% similarity]
LayoutTests/inspector/canvas/resolveContext-webgl2-expected.txt [new file with mode: 0644]
LayoutTests/inspector/canvas/resolveContext-webgl2.html [moved from LayoutTests/inspector/canvas/resolveCanvasContext-webgl2.html with 79% similarity]
LayoutTests/inspector/canvas/resolveContext-webgpu-expected.txt [new file with mode: 0644]
LayoutTests/inspector/canvas/resolveContext-webgpu.html [new file with mode: 0644]
LayoutTests/inspector/canvas/resources/create-context-utilities.js
LayoutTests/platform/gtk/TestExpectations
LayoutTests/platform/ios/TestExpectations
LayoutTests/platform/mac-wk1/TestExpectations
LayoutTests/platform/mac/TestExpectations
LayoutTests/platform/win/TestExpectations
LayoutTests/platform/wincairo/TestExpectations
LayoutTests/platform/wpe/TestExpectations
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/protocol/Canvas.json
Source/WebCore/ChangeLog
Source/WebCore/Modules/webgpu/GPUCanvasContext.cpp
Source/WebCore/Modules/webgpu/GPUCanvasContext.h
Source/WebCore/Modules/webgpu/WebGPUAdapter.cpp
Source/WebCore/Modules/webgpu/WebGPUDevice.cpp
Source/WebCore/Modules/webgpu/WebGPUDevice.h
Source/WebCore/Modules/webgpu/WebGPUDevice.idl
Source/WebCore/inspector/InspectorCanvas.cpp
Source/WebCore/inspector/InspectorCanvas.h
Source/WebCore/inspector/InspectorInstrumentation.cpp
Source/WebCore/inspector/InspectorInstrumentation.h
Source/WebCore/inspector/InspectorShaderProgram.cpp
Source/WebCore/inspector/agents/InspectorCanvasAgent.cpp
Source/WebCore/inspector/agents/InspectorCanvasAgent.h
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Controllers/CanvasManager.js
Source/WebInspectorUI/UserInterface/Models/Canvas.js
Source/WebInspectorUI/UserInterface/Protocol/CanvasObserver.js
Source/WebInspectorUI/UserInterface/Protocol/RemoteObject.js
Source/WebInspectorUI/UserInterface/Views/CanvasContentView.js
Source/WebInspectorUI/UserInterface/Views/CanvasDetailsSidebarPanel.js
Source/WebInspectorUI/UserInterface/Views/CanvasOverviewContentView.js
Source/WebInspectorUI/UserInterface/Views/CanvasTreeElement.js

index c937b85..b1a9be8 100644 (file)
@@ -1,3 +1,45 @@
+2019-09-11  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Canvas: instrument WebGPUDevice instead of GPUCanvasContext
+        https://bugs.webkit.org/show_bug.cgi?id=201650
+
+        Reviewed by Joseph Pecoraro.
+
+        * inspector/canvas/create-context-webgpu.html:
+        * inspector/canvas/create-context-webgpu-expected.txt:
+        * inspector/canvas/resources/create-context-utilities.js:
+        (createDetachedCanvas):
+        (createCSSCanvas):
+        (destroyCanvases):
+
+        * inspector/canvas/requestClientNodes.html: Added.
+        * inspector/canvas/requestClientNodes-expected.txt: Added.
+        * inspector/canvas/requestClientNodes-css.html: Renamed from LayoutTests/inspector/canvas/css-canvas-clients.html.
+        * inspector/canvas/requestClientNodes-css-expected.txt: Renamed from LayoutTests/inspector/canvas/css-canvas-clients-expected.txt.
+        * inspector/canvas/requestClientNodes-webgpu.html: Added.
+        * inspector/canvas/requestClientNodes-webgpu-expected.txt: Added.
+
+        * inspector/canvas/resolveContext-2d.html: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-2d.html.
+        * inspector/canvas/resolveContext-2d-expected.txt: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-2d-expected.txt.
+        * inspector/canvas/resolveContext-bitmaprenderer.html: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-bitmaprenderer.html.
+        * inspector/canvas/resolveContext-bitmaprenderer-expected.txt: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-bitmaprenderer-expected.txt.
+        * inspector/canvas/resolveContext-webgl.html: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-webgl.html.
+        * inspector/canvas/resolveContext-webgl-expected.txt: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-webgl-expected.txt.
+        * inspector/canvas/resolveContext-webgl2.html: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-webgl2.html.
+        * inspector/canvas/resolveContext-webgl2-expected.txt: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-webgl2-expected.txt.
+        * inspector/canvas/resolveContext-webgpu.html: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-webgpu.html.
+        * inspector/canvas/resolveContext-webgpu-expected.txt: Renamed from LayoutTests/inspector/canvas/resolveCanvasContext-webgpu-expected.txt.
+
+        * inspector/canvas/context-attributes-expected.txt:
+
+        * platform/gtk/TestExpectations:
+        * platform/ios/TestExpectations:
+        * platform/mac-wk1/TestExpectations:
+        * platform/mac/TestExpectations:
+        * platform/win/TestExpectations:
+        * platform/wincairo/TestExpectations:
+        * platform/wpe/TestExpectations:
+
 2019-09-11  Chris Dumez  <cdumez@apple.com>
 
         Stop using testRunner.setPrivateBrowsingEnabled_DEPRECATED() in http/tests/cache-storage/cache-persistency.https.html
index 1ec5fc4..02af60a 100644 (file)
@@ -32,6 +32,7 @@ PASS: Canvas context should be "WebGL".
   "antialias": true,
   "premultipliedAlpha": true,
   "preserveDrawingBuffer": false,
+  "powerPreference": "default",
   "failIfMajorPerformanceCaveat": false
 }
 
@@ -45,6 +46,7 @@ PASS: Canvas context should be "WebGL".
   "antialias": true,
   "premultipliedAlpha": true,
   "preserveDrawingBuffer": false,
+  "powerPreference": "default",
   "failIfMajorPerformanceCaveat": false
 }
 PASS: Canvas context should have attribute "alpha" with value "false".
index 934acc3..f19202d 100644 (file)
@@ -5,37 +5,17 @@ Test that CanvasManager tracks creation and destruction of WebGPU canvases.
 -- Running test case: Canvas.CreateContextWebGPU.NoCanvases
 PASS: CanvasManager should have no canvases.
 
--- Running test case: Canvas.CreateContextWebGPU.Attached
+-- Running test case: Canvas.CreateContextWebGPU.Device
 PASS: Canvas context should be Web GPU.
-  0: getContext - [native code]
-  1: createAttachedCanvas - inspector/canvas/resources/create-context-utilities.js:4:36
-  2: Global Code - [program code]
-  3: evaluateWithScopeExtension - [native code]
-  4: (anonymous function) - [native code]
-  5: _wrapCall - [native code]
 
 PASS: Removed canvas has expected ID.
 
--- Running test case: Canvas.CreateContextWebGPU.Detached
-PASS: Canvas context should be Web GPU.
-  0: getContext - [native code]
-  1: createDetachedCanvas - inspector/canvas/resources/create-context-utilities.js:11:62
-  2: Global Code - [program code]
-  3: evaluateWithScopeExtension - [native code]
-  4: (anonymous function) - [native code]
-  5: _wrapCall - [native code]
+-- Running test case: Canvas.CreateContextWebGPU.Canvas.Attached
+PASS: Inspector canvas should not be created for attached GPUCanvasContext without connected WebGPUDevice.
 
-PASS: Removed canvas has expected ID.
+-- Running test case: Canvas.CreateContextWebGPU.Canvas.Detached
+PASS: Inspector canvas should not be created for detached GPUCanvasContext without connected WebGPUDevice.
 
--- Running test case: Canvas.CreateContextWebGPU.CSSCanvas
-Create CSS canvas from -webkit-canvas(css-canvas).
-PASS: Canvas context should be Web GPU.
-  0: getCSSCanvasContext - [native code]
-  1: createCSSCanvas - inspector/canvas/resources/create-context-utilities.js:18:47
-  2: Global Code - [program code]
-  3: evaluateWithScopeExtension - [native code]
-  4: (anonymous function) - [native code]
-  5: _wrapCall - [native code]
-
-PASS: Canvas name should equal the identifier passed to -webkit-canvas.
+-- Running test case: Canvas.CreateContextWebGPU.Canvas.CSS
+PASS: Inspector canvas should not be created for CSS GPUCanvasContext without connected WebGPUDevice.
 
index 0f70cad..bc181f6 100644 (file)
@@ -8,24 +8,80 @@
 if (window.internals)
     window.internals.settings.setWebGPUEnabled(true);
 
-function test() {
-    InspectorTest.debug();
+function createDevice() {
+    function receivedDevice(device) {
+        window.contexts.push(device);
+    }
+
+    function receivedAdapter(adapter) {
+        adapter.requestDevice().then(receivedDevice);
+    }
 
+    navigator.gpu.requestAdapter().then(receivedAdapter);
+}
+
+function test() {
     let suite = InspectorTest.CreateContextUtilities.initializeTestSuite("Canvas.CreateContextWebGPU");
 
     InspectorTest.CreateContextUtilities.addSimpleTestCase({
-        name: "Attached",
-        expression: `createAttachedCanvas("gpu")`,
+        name: "Device",
+        expression: `createDevice()`,
         contextType: WI.Canvas.ContextType.WebGPU,
     });
 
-    InspectorTest.CreateContextUtilities.addSimpleTestCase({
-        name: "Detached",
-        expression: `createDetachedCanvas("gpu")`,
-        contextType: WI.Canvas.ContextType.WebGPU,
+    suite.addTestCase({
+        name: "Canvas.CreateContextWebGPU.Canvas.Attached",
+        description: "Ensure that attached GPUCanvasContext aren't tracked as a canvas, instead of the WebGPUDevice.",
+        async test() {
+            let created = false;
+            let listener = WI.canvasManager.addEventListener(WI.CanvasManager.Event.CanvasAdded, (event) => {
+                InspectorTest.assert(event.target.contextType === WI.Canvas.ContextType.WebGPU);
+                created = true;
+            });
+
+            await InspectorTest.evaluateInPage(`createAttachedCanvas("gpu")`)
+
+            WI.canvasManager.removeEventListener(WI.CanvasManager.Event.CanvasAdded, listener);
+
+            InspectorTest.expectFalse(created, "Inspector canvas should not be created for attached GPUCanvasContext without connected WebGPUDevice.");
+        },
     });
 
-    InspectorTest.CreateContextUtilities.addCSSCanvasTestCase(WI.Canvas.ContextType.WebGPU);
+    suite.addTestCase({
+        name: "Canvas.CreateContextWebGPU.Canvas.Detached",
+        description: "Ensure that detached GPUCanvasContext aren't tracked as a canvas, instead of the WebGPUDevice.",
+        async test() {
+            let created = false;
+            let listener = WI.canvasManager.addEventListener(WI.CanvasManager.Event.CanvasAdded, (event) => {
+                InspectorTest.assert(event.target.contextType === WI.Canvas.ContextType.WebGPU);
+                created = true;
+            });
+
+            await InspectorTest.evaluateInPage(`createDetachedCanvas("gpu")`)
+
+            WI.canvasManager.removeEventListener(WI.CanvasManager.Event.CanvasAdded, listener);
+
+            InspectorTest.expectFalse(created, "Inspector canvas should not be created for detached GPUCanvasContext without connected WebGPUDevice.");
+        },
+    });
+
+    suite.addTestCase({
+        name: "Canvas.CreateContextWebGPU.Canvas.CSS",
+        description: "Ensure that CSS GPUCanvasContext aren't tracked as a canvas, instead of the WebGPUDevice.",
+        async test() {
+            let created = false;
+            let listener = WI.canvasManager.addEventListener(WI.CanvasManager.Event.CanvasAdded, (event) => {
+                InspectorTest.assert(event.target.contextType === WI.Canvas.ContextType.WebGPU);
+                created = true;
+            });
+
+            await InspectorTest.evaluateInPage(`createCSSCanvas("gpu", "css-canvas")`)
+
+            WI.canvasManager.removeEventListener(WI.CanvasManager.Event.CanvasAdded, listener);
+
+            InspectorTest.expectFalse(created, "Inspector canvas should not be created for CSS GPUCanvasContext without connected WebGPUDevice.");
+        },
+    });
 
     suite.runTestCasesAndFinish();
 }
diff --git a/LayoutTests/inspector/canvas/css-canvas-clients-expected.txt b/LayoutTests/inspector/canvas/css-canvas-clients-expected.txt
deleted file mode 100644 (file)
index c60eac3..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-Test that CanvasAgent tracks changes in the client nodes of a CSS canvas.
-
-
-== Running test suite: Canvas.CSSCanvasClients
--- Running test case: Canvas.CSSCanvasClients.InitialLoad
-PASS: CanvasManager should have one canvas.
-PASS: Canvas should have CSS name "css-canvas".
-PASS: There should be no client nodes.
-
--- Running test case: Canvas.CSSCanvasClients.Create
-PASS: Canvas with created client should have CSS name "css-canvas".
-PASS: There should be one client node.
-PASS: Client node "div" is valid.
-
--- Running test case: Canvas.CSSCanvasClients.Destroy
-PASS: Canvas with destroyed client should have CSS name "css-canvas".
-PASS: There should be no client nodes.
-
--- Running test case: Canvas.CSSCanvasClients.InvalidCanvasId
-PASS: Should produce an error.
-Error: Missing canvas for given canvasId
-
diff --git a/LayoutTests/inspector/canvas/css-canvas-clients.html b/LayoutTests/inspector/canvas/css-canvas-clients.html
deleted file mode 100644 (file)
index 7fbaf6a..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
-<script>
-function load() {
-    window.context2d = document.getCSSCanvasContext("2d", "css-canvas", 10, 10);
-
-    runTest();
-}
-
-let cssCanvasClients = [];
-
-function createCSSCanvasClient() {
-    cssCanvasClients.push(document.body.appendChild(document.createElement("div")));
-}
-
-function destroyCSSCanvasClients() {
-    for (let cssCanvasClient of cssCanvasClients)
-        cssCanvasClient.remove();
-
-    cssCanvasClients = [];
-
-    setTimeout(() => { GCController.collect(); }, 0);
-}
-
-function test() {
-    let suite = InspectorTest.createAsyncSuite("Canvas.CSSCanvasClients");
-
-    function logClientNodes(clientNodes) {
-        for (let clientNode of clientNodes) {
-            if (clientNode)
-                InspectorTest.pass(`Client node "${clientNode.appropriateSelectorFor()}" is valid.`);
-            else
-                InspectorTest.fail("Invalid client node.");
-        }
-    }
-
-    suite.addTestCase({
-        name: "Canvas.CSSCanvasClients.InitialLoad",
-        description: "Check that the CanvasManager has one CSS canvas initially.",
-        test(resolve, reject) {
-            let canvases = WI.canvasManager.canvases;
-            InspectorTest.expectEqual(canvases.length, 1, "CanvasManager should have one canvas.");
-            if (!canvases.length) {
-                reject("Missing canvas.");
-                return;
-            }
-
-            InspectorTest.expectEqual(canvases[0].cssCanvasName, "css-canvas", `Canvas should have CSS name "css-canvas".`);
-            canvases[0].requestCSSCanvasClientNodes((clientNodes) => {
-                InspectorTest.expectEqual(clientNodes.length, 0, "There should be no client nodes.");
-                logClientNodes(clientNodes);
-                resolve();
-            });
-        }
-    });
-
-    suite.addTestCase({
-        name: "Canvas.CSSCanvasClients.Create",
-        description: "Check that creating a CSS canvas client node is tracked correctly.",
-        test(resolve, reject) {
-            WI.Canvas.awaitEvent(WI.Canvas.Event.CSSCanvasClientNodesChanged)
-            .then((event) => {
-                InspectorTest.expectEqual(event.target.cssCanvasName, "css-canvas", `Canvas with created client should have CSS name "css-canvas".`);
-                event.target.requestCSSCanvasClientNodes((clientNodes) => {
-                    InspectorTest.expectEqual(clientNodes.length, 1, "There should be one client node.");
-                    logClientNodes(clientNodes);
-                    resolve();
-                });
-            });
-
-            InspectorTest.evaluateInPage(`createCSSCanvasClient()`);
-        }
-    });
-
-    suite.addTestCase({
-        name: "Canvas.CSSCanvasClients.Destroy",
-        description: "Check that destroying a CSS canvas client node is tracked correctly.",
-        test(resolve, reject) {
-            WI.Canvas.awaitEvent(WI.Canvas.Event.CSSCanvasClientNodesChanged)
-            .then((event) => {
-                InspectorTest.expectEqual(event.target.cssCanvasName, "css-canvas", `Canvas with destroyed client should have CSS name "css-canvas".`);
-                event.target.requestCSSCanvasClientNodes((clientNodes) => {
-                    InspectorTest.expectEqual(clientNodes.length, 0, "There should be no client nodes.");
-                    logClientNodes(clientNodes);
-                    resolve();
-                });
-            });
-
-            InspectorTest.evaluateInPage(`destroyCSSCanvasClients()`);
-        }
-    });
-
-    // ------
-
-    suite.addTestCase({
-        name: "Canvas.CSSCanvasClients.InvalidCanvasId",
-        description: "Invalid canvas identifiers should cause an error.",
-        test(resolve, reject) {
-            const canvasId = "DOES_NOT_EXIST";
-            CanvasAgent.requestCSSCanvasClientNodes(canvasId, (error, clientNodeIds) => {
-                InspectorTest.expectThat(error, "Should produce an error.");
-                InspectorTest.log("Error: " + error);
-                resolve();
-            });
-        }
-    });
-
-    suite.runTestCasesAndFinish();
-}
-</script>
-<style>
-    div {
-        width: 10px;
-        height: 10px;
-        background-image: -webkit-canvas(css-canvas);
-    }
-</style>
-</head>
-<body onload="load()">
-    <p>Test that CanvasAgent tracks changes in the client nodes of a CSS canvas.</p>
-</body>
-</html>
diff --git a/LayoutTests/inspector/canvas/requestClientNodes-css-expected.txt b/LayoutTests/inspector/canvas/requestClientNodes-css-expected.txt
new file mode 100644 (file)
index 0000000..82ac63c
--- /dev/null
@@ -0,0 +1,13 @@
+Test that CanvasAgent tracks changes in the client nodes of a CSS canvas.
+
+
+== Running test suite: Canvas.requestClientNodes.CSS
+-- Running test case: Canvas.requestClientNodes.CSS.Create
+PASS: Canvas with created client should have CSS name "css-canvas".
+PASS: There should be one client node.
+PASS: Client node "div" is valid.
+
+-- Running test case: Canvas.requestClientNodes.CSS.Destroy
+PASS: Canvas with destroyed client should have CSS name "css-canvas".
+PASS: There should be no client nodes.
+
diff --git a/LayoutTests/inspector/canvas/requestClientNodes-css.html b/LayoutTests/inspector/canvas/requestClientNodes-css.html
new file mode 100644 (file)
index 0000000..5db14a9
--- /dev/null
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script>
+function load() {
+    window.context2d = document.getCSSCanvasContext("2d", "css-canvas", 10, 10);
+
+    runTest();
+}
+
+let clients = [];
+
+function createClient() {
+    clients.push(document.body.appendChild(document.createElement("div")));
+}
+
+function destroyClients() {
+    for (let client of clients)
+        client.remove();
+
+    clients = [];
+
+    setTimeout(() => { GCController.collect(); }, 0);
+}
+
+function test() {
+    let suite = InspectorTest.createAsyncSuite("Canvas.requestClientNodes.CSS");
+
+    function logClientNodes(clientNodes) {
+        for (let clientNode of clientNodes) {
+            if (clientNode)
+                InspectorTest.pass(`Client node "${clientNode.appropriateSelectorFor()}" is valid.`);
+            else
+                InspectorTest.fail("Invalid client node.");
+        }
+    }
+
+    suite.addTestCase({
+        name: "Canvas.requestClientNodes.CSS.Create",
+        description: "Check that creating a CSS canvas client node is tracked correctly.",
+        test(resolve, reject) {
+            WI.Canvas.awaitEvent(WI.Canvas.Event.ClientNodesChanged)
+            .then((event) => {
+                InspectorTest.expectEqual(event.target.cssCanvasName, "css-canvas", `Canvas with created client should have CSS name "css-canvas".`);
+                event.target.requestClientNodes((clientNodes) => {
+                    InspectorTest.expectEqual(clientNodes.length, 1, "There should be one client node.");
+                    logClientNodes(clientNodes);
+                    resolve();
+                });
+            });
+
+            InspectorTest.evaluateInPage(`createClient()`);
+        }
+    });
+
+    suite.addTestCase({
+        name: "Canvas.requestClientNodes.CSS.Destroy",
+        description: "Check that destroying a CSS canvas client node is tracked correctly.",
+        test(resolve, reject) {
+            WI.Canvas.awaitEvent(WI.Canvas.Event.ClientNodesChanged)
+            .then((event) => {
+                InspectorTest.expectEqual(event.target.cssCanvasName, "css-canvas", `Canvas with destroyed client should have CSS name "css-canvas".`);
+                event.target.requestClientNodes((clientNodes) => {
+                    InspectorTest.expectEqual(clientNodes.length, 0, "There should be no client nodes.");
+                    logClientNodes(clientNodes);
+                    resolve();
+                });
+            });
+
+            InspectorTest.evaluateInPage(`destroyClients()`);
+        }
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+<style>
+    div {
+        width: 10px;
+        height: 10px;
+        background-image: -webkit-canvas(css-canvas);
+    }
+</style>
+</head>
+<body onload="load()">
+    <p>Test that CanvasAgent tracks changes in the client nodes of a CSS canvas.</p>
+</body>
+</html>
diff --git a/LayoutTests/inspector/canvas/requestClientNodes-expected.txt b/LayoutTests/inspector/canvas/requestClientNodes-expected.txt
new file mode 100644 (file)
index 0000000..f1f5c34
--- /dev/null
@@ -0,0 +1,11 @@
+Test that CanvasAgent tracks changes in the client nodes of a CSS canvas.
+
+
+== Running test suite: Canvas.requestClientNodes
+-- Running test case: Canvas.requestClientNodes.Initial
+PASS: There should initially be no client nodes.
+
+-- Running test case: Canvas.CSSCanvasClients.InvalidCanvasId
+PASS: Should produce an error.
+Error: Missing canvas for given canvasId
+
diff --git a/LayoutTests/inspector/canvas/requestClientNodes-webgpu-expected.txt b/LayoutTests/inspector/canvas/requestClientNodes-webgpu-expected.txt
new file mode 100644 (file)
index 0000000..babe73c
--- /dev/null
@@ -0,0 +1,9 @@
+z
+Test that CanvasAgent tracks changes in the client nodes of a WebGPU device.
+
+
+== Running test suite: Canvas.requestClientNodes.WebGPU
+-- Running test case: Canvas.requestClientNodes.WebGPU.Create
+PASS: There should be one client node.
+PASS: Client node "canvas" is valid.
+
diff --git a/LayoutTests/inspector/canvas/requestClientNodes-webgpu.html b/LayoutTests/inspector/canvas/requestClientNodes-webgpu.html
new file mode 100644 (file)
index 0000000..1eb33fe
--- /dev/null
@@ -0,0 +1,54 @@
+z<!DOCTYPE html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script>
+if (window.internals)
+    window.internals.settings.setWebGPUEnabled(true);
+
+async function createClient() {
+    let adapter = await navigator.gpu.requestAdapter();
+    let device = await adapter.requestDevice();
+
+    let context = document.createElement("canvas").getContext("gpu");
+    context.configureSwapChain({device, format: "bgra8unorm"});
+}
+
+function test() {
+    let suite = InspectorTest.createAsyncSuite("Canvas.requestClientNodes.WebGPU");
+
+    function logClientNodes(clientNodes) {
+        for (let clientNode of clientNodes) {
+            if (clientNode)
+                InspectorTest.pass(`Client node "${clientNode.appropriateSelectorFor()}" is valid.`);
+            else
+                InspectorTest.fail("Invalid client node.");
+        }
+    }
+
+    suite.addTestCase({
+        name: "Canvas.requestClientNodes.WebGPU.Create",
+        description: "Check that creating a WebGPU device client node is tracked correctly.",
+        test(resolve, reject) {
+            WI.Canvas.awaitEvent(WI.Canvas.Event.ClientNodesChanged)
+            .then((event) => {
+                InspectorTest.assert(event.target.contextType === WI.Canvas.ContextType.WebGPU);
+                event.target.requestClientNodes((clientNodes) => {
+                    InspectorTest.expectEqual(clientNodes.length, 1, "There should be one client node.");
+                    logClientNodes(clientNodes);
+                    resolve();
+                });
+            });
+
+            InspectorTest.evaluateInPage(`createClient()`).catch(reject);
+        }
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+    <p>Test that CanvasAgent tracks changes in the client nodes of a WebGPU device.</p>
+</body>
+</html>
diff --git a/LayoutTests/inspector/canvas/requestClientNodes.html b/LayoutTests/inspector/canvas/requestClientNodes.html
new file mode 100644 (file)
index 0000000..23cd048
--- /dev/null
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script>
+function load() {
+    window.context2d = document.createElement("canvas").getContext("2d");
+
+    runTest();
+}
+
+function test() {
+    let suite = InspectorTest.createAsyncSuite("Canvas.requestClientNodes");
+
+    suite.addTestCase({
+        name: "Canvas.requestClientNodes.Initial",
+        description: "Check that creating a CSS canvas client node is tracked correctly.",
+        test(resolve, reject) {
+            let canvas = WI.canvasManager.canvases[0];
+            InspectorTest.assert(canvas, "There should be at least one canvas.");
+
+            canvas.requestClientNodes((clientNodes) => {
+                InspectorTest.expectEqual(clientNodes.length, 0, "There should initially be no client nodes.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "Canvas.CSSCanvasClients.InvalidCanvasId",
+        description: "Invalid canvas identifiers should cause an error.",
+        test(resolve, reject) {
+            const canvasId = "DOES_NOT_EXIST";
+            CanvasAgent.requestClientNodes(canvasId, (error, clientNodeIds) => {
+                InspectorTest.expectThat(error, "Should produce an error.");
+                InspectorTest.log("Error: " + error);
+                resolve();
+            });
+        }
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="load()">
+    <p>Test that CanvasAgent tracks changes in the client nodes of a CSS canvas.</p>
+</body>
+</html>
diff --git a/LayoutTests/inspector/canvas/resolveCanvasContext-2d-expected.txt b/LayoutTests/inspector/canvas/resolveCanvasContext-2d-expected.txt
deleted file mode 100644 (file)
index 75d933c..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-Tests for the Canvas.resolveCanvasContext command for 2D contexts.
-
-
-== Running test suite: Canvas.resolveCanvasContext2D
--- Running test case: Canvas.resolveCanvasContext2D.validIdentifier
-PASS: Payload should have type "object".
-PASS: Payload should have className "CanvasRenderingContext2D".
-
--- Running test case: Canvas.resolveCanvasContext.invalidIdentifier
-PASS: Should produce an error.
-Error: Missing canvas for given canvasId
-
diff --git a/LayoutTests/inspector/canvas/resolveCanvasContext-bitmaprenderer-expected.txt b/LayoutTests/inspector/canvas/resolveCanvasContext-bitmaprenderer-expected.txt
deleted file mode 100644 (file)
index 63b7df0..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-Tests for the Canvas.resolveCanvasContext command for BitmapRenderer contexts.
-
-
-== Running test suite: Canvas.resolveCanvasContextBitmapRenderer
--- Running test case: Canvas.resolveCanvasContextBitmapRenderer.validIdentifier
-PASS: Payload should have type "object".
-PASS: Payload should have className "ImageBitmapRenderingContext".
-
diff --git a/LayoutTests/inspector/canvas/resolveCanvasContext-webgl-expected.txt b/LayoutTests/inspector/canvas/resolveCanvasContext-webgl-expected.txt
deleted file mode 100644 (file)
index 9ab62a2..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-Tests for the Canvas.resolveCanvasContext command for WebGL contexts.
-
-
-== Running test suite: Canvas.resolveCanvasContextWebGL
--- Running test case: Canvas.resolveCanvasContextWebGL.validIdentifier
-PASS: Payload should have type "object".
-PASS: Payload should have className "WebGLRenderingContext".
-
diff --git a/LayoutTests/inspector/canvas/resolveCanvasContext-webgl2-expected.txt b/LayoutTests/inspector/canvas/resolveCanvasContext-webgl2-expected.txt
deleted file mode 100644 (file)
index 38c34c2..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-Tests for the Canvas.resolveCanvasContext command for WebGL2 contexts.
-
-
-== Running test suite: Canvas.resolveCanvasContextWebGL2
--- Running test case: Canvas.resolveCanvasContextWebGL2.validIdentifier
-PASS: Payload should have type "object".
-PASS: Payload should have className "WebGL2RenderingContext".
-
diff --git a/LayoutTests/inspector/canvas/resolveCanvasContext-webgpu-expected.txt b/LayoutTests/inspector/canvas/resolveCanvasContext-webgpu-expected.txt
deleted file mode 100644 (file)
index 59eddab..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-Tests for the Canvas.resolveCanvasContext command for WebGPU contexts.
-
-
-== Running test suite: Canvas.resolveCanvasContextWebGPU
--- Running test case: Canvas.resolveCanvasContextWebGPU.validIdentifier
-PASS: Payload should have type "object".
-PASS: Payload should have className "GPUCanvasContext".
-
diff --git a/LayoutTests/inspector/canvas/resolveCanvasContext-webgpu.html b/LayoutTests/inspector/canvas/resolveCanvasContext-webgpu.html
deleted file mode 100644 (file)
index a75d993..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
-<script>
-if (window.internals)
-    window.internals.settings.setWebGPUEnabled(true);
-
-function load() {
-    window.contextWebGPU = document.body.appendChild(document.createElement("canvas")).getContext("gpu");
-
-    runTest();
-}
-
-function test()
-{
-    let suite = InspectorTest.createAsyncSuite("Canvas.resolveCanvasContextWebGPU");
-
-    suite.addTestCase({
-        name: `Canvas.resolveCanvasContextWebGPU.validIdentifier`,
-        description: "Should return a valid object for the given canvas identifier.",
-        test(resolve, reject) {
-            let canvas = WI.canvasManager.canvases.find((canvas) => canvas.contextType === WI.Canvas.ContextType.WebGPU);
-            if (!canvas) {
-                reject(`Missing Canvas.`);
-                return;
-            }
-
-            const objectGroup = "test";
-            CanvasAgent.resolveCanvasContext(canvas.identifier, objectGroup)
-            .then(({object}) => {
-                InspectorTest.expectEqual(object.type, "object", `Payload should have type "object".`);
-                InspectorTest.expectEqual(object.className, "GPUCanvasContext", `Payload should have className "GPUCanvasContext".`);
-            })
-            .then(resolve, reject);
-        }
-    });
-
-    suite.runTestCasesAndFinish();
-}
-</script>
-</head>
-<body onload="load()">
-    <p>Tests for the Canvas.resolveCanvasContext command for WebGPU contexts.</p>
-</body>
-</html>
diff --git a/LayoutTests/inspector/canvas/resolveContext-2d-expected.txt b/LayoutTests/inspector/canvas/resolveContext-2d-expected.txt
new file mode 100644 (file)
index 0000000..48c526c
--- /dev/null
@@ -0,0 +1,12 @@
+Tests for the Canvas.resolveContext command for 2D contexts.
+
+
+== Running test suite: Canvas.resolveContext2D
+-- Running test case: Canvas.resolveContext2D.validIdentifier
+PASS: Payload should have type "object".
+PASS: Payload should have className "CanvasRenderingContext2D".
+
+-- Running test case: Canvas.resolveContext.invalidIdentifier
+PASS: Should produce an error.
+Error: Missing canvas for given canvasId
+
@@ -11,10 +11,10 @@ function load() {
 
 function test()
 {
-    let suite = InspectorTest.createAsyncSuite("Canvas.resolveCanvasContext2D");
+    let suite = InspectorTest.createAsyncSuite("Canvas.resolveContext2D");
 
     suite.addTestCase({
-        name: `Canvas.resolveCanvasContext2D.validIdentifier`,
+        name: `Canvas.resolveContext2D.validIdentifier`,
         description: "Should return a valid object for the given canvas identifier.",
         test(resolve, reject) {
             let canvas = WI.canvasManager.canvases.find((canvas) => canvas.contextType === WI.Canvas.ContextType.Canvas2D);
@@ -24,7 +24,7 @@ function test()
             }
 
             const objectGroup = "test";
-            CanvasAgent.resolveCanvasContext(canvas.identifier, objectGroup)
+            CanvasAgent.resolveContext(canvas.identifier, objectGroup)
             .then(({object}) => {
                 InspectorTest.expectEqual(object.type, "object", `Payload should have type "object".`);
                 InspectorTest.expectEqual(object.className, "CanvasRenderingContext2D", `Payload should have className "CanvasRenderingContext2D".`);
@@ -36,12 +36,12 @@ function test()
     // ------
 
     suite.addTestCase({
-        name: "Canvas.resolveCanvasContext.invalidIdentifier",
+        name: "Canvas.resolveContext.invalidIdentifier",
         description: "Invalid canvas identifiers should cause an error.",
         test(resolve, reject) {
             const identifier = "DOES_NOT_EXIST";
             const objectGroup = "test";
-            CanvasAgent.resolveCanvasContext(identifier, objectGroup, (error) => {
+            CanvasAgent.resolveContext(identifier, objectGroup, (error) => {
                 InspectorTest.expectThat(error, "Should produce an error.");
                 InspectorTest.log("Error: " + error);
                 resolve();
@@ -54,6 +54,6 @@ function test()
 </script>
 </head>
 <body onload="load()">
-    <p>Tests for the Canvas.resolveCanvasContext command for 2D contexts.</p>
+    <p>Tests for the Canvas.resolveContext command for 2D contexts.</p>
 </body>
 </html>
diff --git a/LayoutTests/inspector/canvas/resolveContext-bitmaprenderer-expected.txt b/LayoutTests/inspector/canvas/resolveContext-bitmaprenderer-expected.txt
new file mode 100644 (file)
index 0000000..bd9fed5
--- /dev/null
@@ -0,0 +1,8 @@
+Tests for the Canvas.resolveContext command for BitmapRenderer contexts.
+
+
+== Running test suite: Canvas.resolveContextBitmapRenderer
+-- Running test case: Canvas.resolveContextBitmapRenderer.validIdentifier
+PASS: Payload should have type "object".
+PASS: Payload should have className "ImageBitmapRenderingContext".
+
@@ -11,10 +11,10 @@ function load() {
 
 function test()
 {
-    let suite = InspectorTest.createAsyncSuite("Canvas.resolveCanvasContextBitmapRenderer");
+    let suite = InspectorTest.createAsyncSuite("Canvas.resolveContextBitmapRenderer");
 
     suite.addTestCase({
-        name: `Canvas.resolveCanvasContextBitmapRenderer.validIdentifier`,
+        name: `Canvas.resolveContextBitmapRenderer.validIdentifier`,
         description: "Should return a valid object for the given canvas identifier.",
         test(resolve, reject) {
             let canvas = WI.canvasManager.canvases.find((canvas) => canvas.contextType === WI.Canvas.ContextType.BitmapRenderer);
@@ -24,7 +24,7 @@ function test()
             }
 
             const objectGroup = "test";
-            CanvasAgent.resolveCanvasContext(canvas.identifier, objectGroup)
+            CanvasAgent.resolveContext(canvas.identifier, objectGroup)
             .then(({object}) => {
                 InspectorTest.expectEqual(object.type, "object", `Payload should have type "object".`);
                 InspectorTest.expectEqual(object.className, "ImageBitmapRenderingContext", `Payload should have className "ImageBitmapRenderingContext".`);
@@ -38,6 +38,6 @@ function test()
 </script>
 </head>
 <body onload="load()">
-    <p>Tests for the Canvas.resolveCanvasContext command for BitmapRenderer contexts.</p>
+    <p>Tests for the Canvas.resolveContext command for BitmapRenderer contexts.</p>
 </body>
 </html>
diff --git a/LayoutTests/inspector/canvas/resolveContext-webgl-expected.txt b/LayoutTests/inspector/canvas/resolveContext-webgl-expected.txt
new file mode 100644 (file)
index 0000000..64a0a2e
--- /dev/null
@@ -0,0 +1,8 @@
+Tests for the Canvas.resolveContext command for WebGL contexts.
+
+
+== Running test suite: Canvas.resolveContextWebGL
+-- Running test case: Canvas.resolveContextWebGL.validIdentifier
+PASS: Payload should have type "object".
+PASS: Payload should have className "WebGLRenderingContext".
+
@@ -11,10 +11,10 @@ function load() {
 
 function test()
 {
-    let suite = InspectorTest.createAsyncSuite("Canvas.resolveCanvasContextWebGL");
+    let suite = InspectorTest.createAsyncSuite("Canvas.resolveContextWebGL");
 
     suite.addTestCase({
-        name: `Canvas.resolveCanvasContextWebGL.validIdentifier`,
+        name: `Canvas.resolveContextWebGL.validIdentifier`,
         description: "Should return a valid object for the given canvas identifier.",
         test(resolve, reject) {
             let canvas = WI.canvasManager.canvases.find((canvas) => canvas.contextType === WI.Canvas.ContextType.WebGL);
@@ -24,7 +24,7 @@ function test()
             }
 
             const objectGroup = "test";
-            CanvasAgent.resolveCanvasContext(canvas.identifier, objectGroup)
+            CanvasAgent.resolveContext(canvas.identifier, objectGroup)
             .then(({object}) => {
                 InspectorTest.expectEqual(object.type, "object", `Payload should have type "object".`);
                 InspectorTest.expectEqual(object.className, "WebGLRenderingContext", `Payload should have className "WebGLRenderingContext".`);
@@ -38,6 +38,6 @@ function test()
 </script>
 </head>
 <body onload="load()">
-    <p>Tests for the Canvas.resolveCanvasContext command for WebGL contexts.</p>
+    <p>Tests for the Canvas.resolveContext command for WebGL contexts.</p>
 </body>
 </html>
diff --git a/LayoutTests/inspector/canvas/resolveContext-webgl2-expected.txt b/LayoutTests/inspector/canvas/resolveContext-webgl2-expected.txt
new file mode 100644 (file)
index 0000000..e56db2e
--- /dev/null
@@ -0,0 +1,8 @@
+Tests for the Canvas.resolveContext command for WebGL2 contexts.
+
+
+== Running test suite: Canvas.resolveContextWebGL2
+-- Running test case: Canvas.resolveContextWebGL2.validIdentifier
+PASS: Payload should have type "object".
+PASS: Payload should have className "WebGL2RenderingContext".
+
@@ -14,10 +14,10 @@ function load() {
 
 function test()
 {
-    let suite = InspectorTest.createAsyncSuite("Canvas.resolveCanvasContextWebGL2");
+    let suite = InspectorTest.createAsyncSuite("Canvas.resolveContextWebGL2");
 
     suite.addTestCase({
-        name: `Canvas.resolveCanvasContextWebGL2.validIdentifier`,
+        name: `Canvas.resolveContextWebGL2.validIdentifier`,
         description: "Should return a valid object for the given canvas identifier.",
         test(resolve, reject) {
             let canvas = WI.canvasManager.canvases.find((canvas) => canvas.contextType === WI.Canvas.ContextType.WebGL2);
@@ -27,7 +27,7 @@ function test()
             }
 
             const objectGroup = "test";
-            CanvasAgent.resolveCanvasContext(canvas.identifier, objectGroup)
+            CanvasAgent.resolveContext(canvas.identifier, objectGroup)
             .then(({object}) => {
                 InspectorTest.expectEqual(object.type, "object", `Payload should have type "object".`);
                 InspectorTest.expectEqual(object.className, "WebGL2RenderingContext", `Payload should have className "WebGL2RenderingContext".`);
@@ -41,6 +41,6 @@ function test()
 </script>
 </head>
 <body onload="load()">
-    <p>Tests for the Canvas.resolveCanvasContext command for WebGL2 contexts.</p>
+    <p>Tests for the Canvas.resolveContext command for WebGL2 contexts.</p>
 </body>
 </html>
diff --git a/LayoutTests/inspector/canvas/resolveContext-webgpu-expected.txt b/LayoutTests/inspector/canvas/resolveContext-webgpu-expected.txt
new file mode 100644 (file)
index 0000000..e616922
--- /dev/null
@@ -0,0 +1,8 @@
+Tests for the Canvas.resolveContext command for WebGPU devices.
+
+
+== Running test suite: Canvas.resolveContextWebGPU
+-- Running test case: Canvas.resolveContextWebGPU.validIdentifier
+PASS: Payload should have type "object".
+PASS: Payload should have className "GPUDevice".
+
diff --git a/LayoutTests/inspector/canvas/resolveContext-webgpu.html b/LayoutTests/inspector/canvas/resolveContext-webgpu.html
new file mode 100644 (file)
index 0000000..dc00ef1
--- /dev/null
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script>
+if (window.internals)
+    window.internals.settings.setWebGPUEnabled(true);
+
+function createDevice() {
+    navigator.gpu.requestAdapter().then(function adapterRequested(adapter) {
+        adapter.requestDevice().then(function deviceRequested(device) {
+            window.contextWebGPU = device;
+        });
+    });
+}
+
+function test()
+{
+    let suite = InspectorTest.createAsyncSuite("Canvas.resolveContextWebGPU");
+
+    suite.addTestCase({
+        name: `Canvas.resolveContextWebGPU.validIdentifier`,
+        description: "Should return a valid object for the given canvas identifier.",
+        async test() {
+            InspectorTest.assert(!WI.canvasManager.canvases.length, "There should be no canvases.");
+
+            let [canvasAddedEvent] = await Promise.all([
+                WI.canvasManager.awaitEvent(WI.CanvasManager.Event.CanvasAdded),
+                InspectorTest.evaluateInPage(`createDevice()`),
+            ]);
+
+            let {canvas} = canvasAddedEvent.data;
+            InspectorTest.assert(canvas.contextType, WI.Canvas.ContextType.WebGPU, "Added canvas should be a WebGPU device.");
+
+            const objectGroup = "test";
+            let {object} = await CanvasAgent.resolveContext(canvas.identifier, objectGroup)
+            InspectorTest.expectEqual(object.type, "object", `Payload should have type "object".`);
+            InspectorTest.expectEqual(object.className, "GPUDevice", `Payload should have className "GPUDevice".`);
+        }
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+    <p>Tests for the Canvas.resolveContext command for WebGPU devices.</p>
+</body>
+</html>
index d6cc55d..3a46045 100644 (file)
@@ -1,29 +1,29 @@
-let contexts = [];
+window.contexts = [];
 
 function createAttachedCanvas(contextType) {
     let canvas = document.body.appendChild(document.createElement("canvas"));
     let context = canvas.getContext(contextType);
     if (!context)
         TestPage.addResult("FAIL: missing context for type " + contextType);
-    contexts.push(context);
+    window.contexts.push(context);
 }
 
 function createDetachedCanvas(contextType) {
     let context = document.createElement("canvas").getContext(contextType);
     if (!context)
         TestPage.addResult("FAIL: missing context for type " + contextType);
-    contexts.push(context);
+    window.contexts.push(context);
 }
 
 function createCSSCanvas(contextType, canvasName) {
     let context = document.getCSSCanvasContext(contextType, canvasName, 10, 10);
     if (!context)
         TestPage.addResult("FAIL: missing context for type " + contextType);
-    contexts.push();
+    window.contexts.push();
 }
 
 function destroyCanvases() {
-    for (let context of contexts) {
+    for (let context of window.contexts) {
         if (!context)
             continue;
 
@@ -32,7 +32,7 @@ function destroyCanvases() {
             canvasElement.remove();
     }
 
-    contexts = [];
+    window.contexts = [];
 
     // Force GC to make sure the canvas element is destroyed, otherwise the frontend
     // does not receive WI.CanvasManager.Event.CanvasRemoved events.
index 6f7b643..f9c1d06 100644 (file)
@@ -516,7 +516,7 @@ webkit.org/b/166536 inspector/canvas/recording-webgl2-full.html [ Skip ]
 webkit.org/b/166536 inspector/canvas/recording-webgl2-memoryLimit.html [ Skip ]
 webkit.org/b/166536 inspector/canvas/recording-webgl2-snapshots.html [ Skip ]
 webkit.org/b/166536 inspector/canvas/requestContent-webgl2.html [ Skip ]
-webkit.org/b/166536 inspector/canvas/resolveCanvasContext-webgl2.html [ Skip ]
+webkit.org/b/166536 inspector/canvas/resolveContext-webgl2.html [ Skip ]
 webkit.org/b/166536 inspector/canvas/shaderProgram-add-remove-webgl2.html [ Skip ]
 webkit.org/b/166536 webgl/2.0.0/ [ Skip ]
 
@@ -1150,7 +1150,8 @@ webkit.org/b/168466 http/tests/security/bypassing-cors-checks-for-extension-urls
 # No support for WebGPU yet
 webkit.org/b/191005 webgpu/ [ Skip ]
 webkit.org/b/191005 inspector/canvas/create-context-webgpu.html [ Skip ]
-webkit.org/b/191005 inspector/canvas/resolveCanvasContext-webgpu.html [ Skip ]
+webkit.org/b/191005 inspector/canvas/requestClientNodes-webgpu.html [ Skip ]
+webkit.org/b/191005 inspector/canvas/resolveContext-webgpu.html [ Skip ]
 
 # No support for resource load statistics yet
 http/tests/resourceLoadStatistics/ [ Skip ]
index b966aec..dfb0528 100644 (file)
@@ -44,7 +44,8 @@ webkit.org/b/200308 fast/events/touch/ios/content-observation/non-visible-conten
 # WebGPU is not enabled on iOS Simulator.
 webgpu [ Skip ]
 inspector/canvas/create-context-webgpu.html [ Skip ]
-inspector/canvas/resolveCanvasContext-webgpu.html [ Skip ]
+inspector/canvas/requestClientNodes-webgpu.html [ Skip ]
+inspector/canvas/resolveContext-webgpu.html [ Skip ]
 
 # Encrypted Media Extensions are not enabled
 media/encrypted-media/
index 930b39b..1bcc457 100644 (file)
@@ -46,7 +46,8 @@ css-dark-mode [ Skip ]
 
 webgpu [ Skip ]
 inspector/canvas/create-context-webgpu.html [ Skip ]
-inspector/canvas/resolveCanvasContext-webgpu.html [ Skip ]
+inspector/canvas/requestClientNodes-webgpu.html [ Skip ]
+inspector/canvas/resolveContext-webgpu.html [ Skip ]
 
 # Media Stream API testing is not supported for WK1 yet.
 fast/mediastream
index ec77b62..12b0eaf 100644 (file)
@@ -1032,11 +1032,11 @@ webkit.org/b/174066 inspector/canvas/console-record-webgl2.html [ Pass Timeout ]
 webkit.org/b/173931 inspector/canvas/context-attributes.html [ Pass Timeout ]
 webkit.org/b/178028 inspector/canvas/create-context-2d.html [ Pass Timeout ]
 webkit.org/b/174066 inspector/canvas/create-context-webgl2.html [ Pass Timeout ]
-webkit.org/b/174272 inspector/canvas/css-canvas-clients.html [ Pass Failure ]
 webkit.org/b/174066 inspector/canvas/recording-webgl2-frameCount.html [ Pass Failure Timeout ]
 webkit.org/b/174066 inspector/canvas/recording-webgl2-full.html [ Pass Failure Timeout ]
 webkit.org/b/174066 inspector/canvas/recording-webgl2-memoryLimit.html [ Pass Failure Timeout ]
 webkit.org/b/174066 inspector/canvas/recording-webgl2-snapshots.html [ Pass Failure Timeout ]
+webkit.org/b/174272 inspector/canvas/requestClientNodes-css.html [ Pass Failure ]
 webkit.org/b/174066 inspector/canvas/shaderProgram-add-remove-webgl2.html [ Pass Failure Timeout ]
 webkit.org/b/160048 [ Debug ] inspector/codemirror/prettyprinting-javascript.html [ Pass Timeout ]
 webkit.org/b/152025 [ Debug ] inspector/console/messagesCleared.html [ Pass Timeout ]
@@ -1783,7 +1783,8 @@ webkit.org/b/190976 imported/w3c/web-platform-tests/media-source/mediasource-cha
 
 webkit.org/b/199275 [ HighSierra ] webgpu [ Skip ]
 webkit.org/b/199275 [ HighSierra ] inspector/canvas/create-context-webgpu.html [ Skip ]
-webkit.org/b/199275 [ HighSierra ] inspector/canvas/resolveCanvasContext-webgpu.html [ Skip ]
+webkit.org/b/199275 [ HighSierra ] inspector/canvas/requestClientNodes-webgpu.html [ Skip ]
+webkit.org/b/199275 [ HighSierra ] inspector/canvas/resolveContext-webgpu.html [ Skip ]
 
 webkit.org/b/189680 platform/mac/media/audio-session-category-video-paused.html [ Pass Timeout ]
 
index 11d851c..725e685 100644 (file)
@@ -2001,8 +2001,8 @@ inspector/canvas/recording-webgl2-snapshots.html [ Skip ]
 inspector/canvas/requestContent-webgl.html [ Skip ]
 inspector/canvas/requestContent-webgl2.html [ Skip ]
 inspector/canvas/requestShaderSource.html [ Skip ]
-inspector/canvas/resolveCanvasContext-webgl.html [ Skip ]
-inspector/canvas/resolveCanvasContext-webgl2.html [ Skip ]
+inspector/canvas/resolveContext-webgl.html [ Skip ]
+inspector/canvas/resolveContext-webgl2.html [ Skip ]
 inspector/canvas/shaderProgram-add-remove-webgl.html [ Skip ]
 inspector/canvas/shaderProgram-add-remove-webgl2.html [ Skip ]
 inspector/canvas/updateShader.html [ Skip ]
@@ -4244,7 +4244,8 @@ webkit.org/b/190520 editing/pasteboard/copy-paste-across-shadow-boundaries-with-
 # WebGPU is not supported on Windows
 webgpu [ Skip ]
 inspector/canvas/create-context-webgpu.html [ Skip ]
-inspector/canvas/resolveCanvasContext-webgpu.html [ Skip ]
+inspector/canvas/requestClientNodes-webgpu.html [ Skip ]
+inspector/canvas/resolveContext-webgpu.html [ Skip ]
 
 webkit.org/b/191194 fast/block/basic/inline-content-with-floating-image.html [ Failure ]
 webkit.org/b/191194 fast/block/basic/inline-content-with-floating-images2.html [ Failure ]
index e290e08..441ac51 100644 (file)
@@ -289,7 +289,7 @@ inspector/canvas/requestContent-webgl2.html [ Skip ]
 inspector/canvas/create-context-webgl2.html [ Skip ]
 inspector/canvas/recording-webgl2.html [ Skip ]
 inspector/canvas/recording-webgl2-snapshots.html [ Skip ]
-inspector/canvas/resolveCanvasContext-webgl2.html [ Skip ]
+inspector/canvas/resolveContext-webgl2.html [ Skip ]
 inspector/canvas/shaderProgram-add-remove-webgl2.html [ Skip ]
 webgl/webgl2-rendering-context-defined.html [ Skip ]
 webgl/webgl2-rendering-context-obtain.html [ Skip ]
@@ -297,7 +297,8 @@ webgl/webgl2-rendering-context-obtain.html [ Skip ]
 # WEBGPU is disabled
 webgpu [ Skip ]
 inspector/canvas/create-context-webgpu.html [ Skip ]
-inspector/canvas/resolveCanvasContext-webgpu.html [ Skip ]
+inspector/canvas/requestClientNodes-webgpu.html [ Skip ]
+inspector/canvas/resolveContext-webgpu.html [ Skip ]
 
 # WIRELESS_PLAYBACK_TARGET is disabled
 media/airplay-target-availability.html [ Skip ]
index ae5b684..d5f85ed 100644 (file)
@@ -302,7 +302,8 @@ Bug(WPE) js/stringimpl-to-jsstring-on-large-strings-3.html [ Skip ]
 # No WebGPU support yet
 webgpu [ Skip ]
 inspector/canvas/create-context-webgpu.html [ Skip ]
-inspector/canvas/resolveCanvasContext-webgpu.html [ Skip ]
+inspector/canvas/requestClientNodes-webgpu.html [ Skip ]
+inspector/canvas/resolveContext-webgpu.html [ Skip ]
 
 # Skipped due to untestable DRM key system. ClearKey counterparts are tested instead.
 imported/w3c/web-platform-tests/encrypted-media/drm-check-initdata-type.https.html [ Skip ]
index 172750e..58c955c 100644 (file)
@@ -1,3 +1,23 @@
+2019-09-11  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Canvas: instrument WebGPUDevice instead of GPUCanvasContext
+        https://bugs.webkit.org/show_bug.cgi?id=201650
+
+        Reviewed by Joseph Pecoraro.
+
+        Most of the actual "work" done with Web GPU actually uses a `WebGPUDevice`.
+
+        A `GPUCanvasContext` is basically just a display "client" of the device, and isn't even
+        required (e.g. compute pipeline).  We should treat the `GPUCanvasContext` almost like a
+        `-webkit-canvas` client of a `WebGPUDevice`.
+
+        * inspector/protocol/Canvas.json:
+         - Add `powerPreference` key to `ContextAttributes` type.
+         - Rename `requestCSSCanvasClientNodes` command to `requestClientNodes` for the above reason.
+         - Rename `cssCanvasClientNodesChanged` event to `clientNodesChanged` for the above reason.
+         - Rename `resolveCanvasContext` command to `resolveContext` since a `WebGPUDevice` isn't
+           really a "canvas".
+
 2019-09-11  Yusuke Suzuki  <ysuzuki@apple.com>
 
         [JSC] Add StringCodePointAt intrinsic
index 8580f87..798b991 100644 (file)
             "type": "object",
             "description": "Drawing surface attributes.",
             "properties": [
-                { "name": "alpha", "type": "boolean", "optional": true },
-                { "name": "depth", "type": "boolean", "optional": true },
-                { "name": "stencil", "type": "boolean", "optional": true },
-                { "name": "antialias", "type": "boolean", "optional": true },
-                { "name": "premultipliedAlpha", "type": "boolean", "optional": true },
-                { "name": "preserveDrawingBuffer", "type": "boolean", "optional": true },
-                { "name": "failIfMajorPerformanceCaveat", "type": "boolean", "optional": true }
+                { "name": "alpha", "type": "boolean", "optional": true, "description": "WebGL, WebGL2, ImageBitmapRenderingContext" },
+                { "name": "depth", "type": "boolean", "optional": true, "description": "WebGL, WebGL2" },
+                { "name": "stencil", "type": "boolean", "optional": true, "description": "WebGL, WebGL2" },
+                { "name": "antialias", "type": "boolean", "optional": true, "description": "WebGL, WebGL2" },
+                { "name": "premultipliedAlpha", "type": "boolean", "optional": true, "description": "WebGL, WebGL2" },
+                { "name": "preserveDrawingBuffer", "type": "boolean", "optional": true, "description": "WebGL, WebGL2" },
+                { "name": "failIfMajorPerformanceCaveat", "type": "boolean", "optional": true, "description": "WebGL, WebGL2" },
+                { "name": "powerPreference", "type": "string", "optional": true, "description": "WebGL, WebGL2, WebGPU" }
             ]
         },
         {
@@ -84,8 +85,8 @@
             ]
         },
         {
-            "name": "requestCSSCanvasClientNodes",
-            "description": "Gets all the nodes that are using this canvas via -webkit-canvas.",
+            "name": "requestClientNodes",
+            "description": "Gets all <code>-webkit-canvas</code> nodes or active <code>HTMLCanvasElement</code> for a <code>WebGPUDevice</code>.",
             "parameters": [
                 { "name": "canvasId", "$ref": "CanvasId" }
             ],
@@ -94,8 +95,8 @@
             ]
         },
         {
-            "name": "resolveCanvasContext",
-            "description": "Resolves JavaScript canvas context object for given canvasId.",
+            "name": "resolveContext",
+            "description": "Resolves JavaScript canvas/device context object for given canvasId.",
             "parameters": [
                 { "name": "canvasId", "$ref": "CanvasId", "description": "Canvas identifier." },
                 { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }
             ]
         },
         {
-            "name": "cssCanvasClientNodesChanged",
+            "name": "clientNodesChanged",
             "parameters": [
                 { "name": "canvasId", "$ref": "CanvasId", "description": "Identifier of canvas that changed." }
             ]
index 235ac51..c344056 100644 (file)
@@ -1,3 +1,93 @@
+2019-09-11  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Canvas: instrument WebGPUDevice instead of GPUCanvasContext
+        https://bugs.webkit.org/show_bug.cgi?id=201650
+
+        Reviewed by Joseph Pecoraro.
+
+        Most of the actual "work" done with Web GPU actually uses a `WebGPUDevice`.
+
+        A `GPUCanvasContext` is basically just a display "client" of the device, and isn't even
+        required (e.g. compute pipeline).  We should treat the `GPUCanvasContext` almost like a
+        `-webkit-canvas` client of a `WebGPUDevice`.
+
+        Tests: inspector/canvas/create-context-webgpu.html
+               inspector/canvas/requestClientNodes-webgpu.html
+               inspector/canvas/resolveContext-webgpu.html
+
+        * Modules/webgpu/WebGPUAdapter.cpp:
+        (WebCore::WebGPUAdapter::requestDevice const):
+        Notify web inspector after a device is created.
+
+        * Modules/webgpu/WebGPUDevice.idl:
+        * Modules/webgpu/WebGPUDevice.h:
+        * Modules/webgpu/WebGPUDevice.cpp:
+        (WebCore::WebGPUDevice::instances): Added.
+        (WebCore::WebGPUDevice::instancesMutex): Added.
+        (WebCore::WebGPUDevice::~WebGPUDevice): Added.
+        Notify web inspector when the device is about to be destructed.
+
+        * Modules/webgpu/GPUCanvasContext.h:
+        * Modules/webgpu/GPUCanvasContext.cpp:
+        (WebCore::GPUCanvasContext::create):
+        (WebCore::GPUCanvasContext::configureSwapChain):
+
+        * inspector/InspectorCanvas.h:
+        * inspector/InspectorCanvas.cpp:
+        (WebCore::canvasIfContextMatchesDevice): Added.
+        (WebCore::InspectorCanvas::create):
+        (WebCore::InspectorCanvas::InspectorCanvas):
+        (WebCore::InspectorCanvas::canvasContext const): Added.
+        (WebCore::InspectorCanvas::canvasElement const): Added.
+        (WebCore::InspectorCanvas::isDeviceForCanvasContext const): Added.
+        (WebCore::InspectorCanvas::deviceContext const): Added.
+        (WebCore::InspectorCanvas::scriptExecutionContext const): Added.
+        (WebCore::InspectorCanvas::resolveContext const): Added.
+        (WebCore::InspectorCanvas::clientNodes const): Added.
+        (WebCore::InspectorCanvas::canvasChanged):
+        (WebCore::InspectorCanvas::resetRecordingData):
+        (WebCore::InspectorCanvas::recordAction):
+        (WebCore::InspectorCanvas::buildObjectForCanvas):
+        (WebCore::InspectorCanvas::releaseObjectForRecording):
+        (WebCore::InspectorCanvas::getCanvasContentAsDataURL):
+        (WebCore::InspectorCanvas::buildInitialState):
+        (WebCore::InspectorCanvas::canvasElement): Deleted.
+        * inspector/InspectorInstrumentation.cpp:
+        (WebCore::InspectorInstrumentation::didCreateWebGPUDeviceImpl): Added.
+        (WebCore::InspectorInstrumentation::willDestroyWebGPUDeviceImpl): Added.
+        (WebCore::InspectorInstrumentation::willConfigureSwapChainImpl): Added.
+        * inspector/InspectorInstrumentation.h:
+        (WebCore::InspectorInstrumentation::didCreateWebGPUDevice): Added.
+        (WebCore::InspectorInstrumentation::willDestroyWebGPUDevice): Added.
+        (WebCore::InspectorInstrumentation::willConfigureSwapChain): Added.
+
+        * inspector/agents/InspectorCanvasAgent.h:
+        * inspector/agents/InspectorCanvasAgent.cpp:
+        (WebCore::InspectorCanvasAgent::enable):
+        (WebCore::InspectorCanvasAgent::requestClientNodes): Added.
+        (WebCore::InspectorCanvasAgent::resolveContext): Added.
+        (WebCore::InspectorCanvasAgent::startRecording):
+        (WebCore::InspectorCanvasAgent::stopRecording):
+        (WebCore::InspectorCanvasAgent::frameNavigated):
+        (WebCore::InspectorCanvasAgent::didChangeCSSCanvasClientNodes):
+        (WebCore::InspectorCanvasAgent::didChangeCanvasMemory):
+        (WebCore::InspectorCanvasAgent::canvasDestroyed):
+        (WebCore::InspectorCanvasAgent::recordCanvasAction):
+        (WebCore::InspectorCanvasAgent::didFinishRecordingCanvasFrame):
+        (WebCore::InspectorCanvasAgent::didCreateWebGPUDevice): Added.
+        (WebCore::InspectorCanvasAgent::willDestroyWebGPUDevice): Added.
+        (WebCore::InspectorCanvasAgent::willConfigureSwapChain): Added.
+        (WebCore::InspectorCanvasAgent::clearCanvasData):
+        (WebCore::InspectorCanvasAgent::bindCanvas):
+        (WebCore::InspectorCanvasAgent::unbindCanvas):
+        (WebCore::InspectorCanvasAgent::findInspectorCanvas):
+        (WebCore::InspectorCanvasAgent::requestCSSCanvasClientNodes): Deleted.
+        (WebCore::contextAsScriptValue): Deleted.
+        (WebCore::InspectorCanvasAgent::resolveCanvasContext): Deleted.
+
+        * inspector/InspectorShaderProgram.cpp:
+        (WebCore::InspectorShaderProgram::context const):
+
 2019-09-11  Chris Dumez  <cdumez@apple.com>
 
         Posting a message to a redundant service worker should fail silently instead of throwing
index bd20979..ea7b2a6 100644 (file)
@@ -41,9 +41,6 @@ std::unique_ptr<GPUCanvasContext> GPUCanvasContext::create(CanvasBase& canvas)
 {
     auto context = std::unique_ptr<GPUCanvasContext>(new GPUCanvasContext(canvas));
     context->suspendIfNeeded();
-
-    InspectorInstrumentation::didCreateCanvasRenderingContext(*context);
-
     return context;
 }
 
@@ -67,8 +64,11 @@ Ref<WebGPUSwapChain> GPUCanvasContext::configureSwapChain(const WebGPUSwapChainD
         // FIXME: Test that this works as expected with error reporting.
         if (m_swapChain)
             m_swapChain->destroy();
-        
+
+        InspectorInstrumentation::willConfigureSwapChain(*this, newSwapChain.get());
+
         m_swapChain = newSwapChain.copyRef();
+
         notifyCanvasContentChanged();
     }
     
index d2c1c3c..25cd17b 100644 (file)
@@ -43,6 +43,7 @@ public:
 
     HTMLCanvasElement& canvas() const { return downcast<HTMLCanvasElement>(canvasBase()); }
 
+    WebGPUSwapChain* swapChain() const { return m_swapChain.get(); }
     Ref<WebGPUSwapChain> configureSwapChain(const WebGPUSwapChainDescriptor&);
 
 private:
index 52714fb..69789d9 100644 (file)
@@ -29,6 +29,7 @@
 #if ENABLE(WEBGPU)
 
 #include "Document.h"
+#include "InspectorInstrumentation.h"
 #include "JSWebGPUDevice.h"
 
 namespace WebCore {
@@ -46,9 +47,11 @@ WebGPUAdapter::WebGPUAdapter(Optional<GPURequestAdapterOptions>&& options)
 void WebGPUAdapter::requestDevice(Document& document, DeviceRequestPromise&& promise) const
 {
     document.postTask([protectedThis = makeRef(*this), promise = WTFMove(promise)] (ScriptExecutionContext& context) mutable {
-        if (auto device = WebGPUDevice::tryCreate(context, protectedThis.get()))
+        if (auto device = WebGPUDevice::tryCreate(context, protectedThis.get())) {
+            InspectorInstrumentation::didCreateWebGPUDevice(*device);
+
             promise.resolve(device.releaseNonNull());
-        else
+        else
             promise.reject();
     });
 }
index b613b5a..fb95ded 100644 (file)
@@ -47,6 +47,7 @@
 #include "GPUShaderModuleDescriptor.h"
 #include "GPUTextureDescriptor.h"
 #include "GPUUncapturedErrorEvent.h"
+#include "InspectorInstrumentation.h"
 #include "JSDOMConvertBufferSource.h"
 #include "JSGPUOutOfMemoryError.h"
 #include "JSGPUValidationError.h"
 #include "WebGPUTexture.h"
 #include <JavaScriptCore/ConsoleMessage.h>
 #include <memory>
+#include <wtf/HashSet.h>
 #include <wtf/IsoMallocInlines.h>
+#include <wtf/Lock.h>
 #include <wtf/MainThread.h>
+#include <wtf/NeverDestroyed.h>
 #include <wtf/Optional.h>
+#include <wtf/Ref.h>
+#include <wtf/RefPtr.h>
 #include <wtf/Variant.h>
+#include <wtf/Vector.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
@@ -90,6 +97,22 @@ RefPtr<WebGPUDevice> WebGPUDevice::tryCreate(ScriptExecutionContext& context, Re
     return nullptr;
 }
 
+HashSet<WebGPUDevice*>& WebGPUDevice::instances(const LockHolder&)
+{
+    static NeverDestroyed<HashSet<WebGPUDevice*>> instances;
+    return instances;
+}
+
+Lock& WebGPUDevice::instancesMutex()
+{
+    static LazyNeverDestroyed<Lock> mutex;
+    static std::once_flag initializeMutex;
+    std::call_once(initializeMutex, [] {
+        mutex.construct();
+    });
+    return mutex.get();
+}
+
 WebGPUDevice::WebGPUDevice(ScriptExecutionContext& context, Ref<const WebGPUAdapter>&& adapter, Ref<GPUDevice>&& device)
     : m_scriptExecutionContext(context)
     , m_adapter(WTFMove(adapter))
@@ -100,6 +123,18 @@ WebGPUDevice::WebGPUDevice(ScriptExecutionContext& context, Ref<const WebGPUAdap
     }))
 {
     ASSERT(m_scriptExecutionContext.isDocument());
+
+    LockHolder lock(instancesMutex());
+    instances(lock).add(this);
+}
+
+WebGPUDevice::~WebGPUDevice()
+{
+    InspectorInstrumentation::willDestroyWebGPUDevice(*this);
+
+    LockHolder lock(instancesMutex());
+    ASSERT(instances(lock).contains(this));
+    instances(lock).remove(this);
 }
 
 Ref<WebGPUBuffer> WebGPUDevice::createBuffer(const GPUBufferDescriptor& descriptor) const
index ce454a9..d98f23c 100644 (file)
 #include "WebGPUAdapter.h"
 #include "WebGPUQueue.h"
 #include "WebGPUSwapChainDescriptor.h"
-#include <wtf/Ref.h>
+#include <wtf/Forward.h>
 #include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
-#include <wtf/Vector.h>
 #include <wtf/WeakPtr.h>
 
 namespace JSC {
@@ -80,8 +78,13 @@ using ErrorPromise = DOMPromiseDeferred<IDLNullable<ErrorIDLUnion>>;
 class WebGPUDevice : public RefCounted<WebGPUDevice>, public EventTargetWithInlineData, public CanMakeWeakPtr<WebGPUDevice> {
     WTF_MAKE_ISO_ALLOCATED(WebGPUDevice);
 public:
+    virtual ~WebGPUDevice();
+
     static RefPtr<WebGPUDevice> tryCreate(ScriptExecutionContext&, Ref<const WebGPUAdapter>&&);
 
+    static HashSet<WebGPUDevice*>& instances(const LockHolder&);
+    static Lock& instancesMutex();
+
     const WebGPUAdapter& adapter() const { return m_adapter.get(); }
     GPUDevice& device() { return m_device.get(); }
     const GPUDevice& device() const { return m_device.get(); }
@@ -106,6 +109,8 @@ public:
     void pushErrorScope(GPUErrorFilter filter) { m_errorScopes->pushErrorScope(filter); }
     void popErrorScope(ErrorPromise&&);
 
+    ScriptExecutionContext* scriptExecutionContext() const final { return &m_scriptExecutionContext; }
+
     using RefCounted::ref;
     using RefCounted::deref;
 
@@ -114,7 +119,6 @@ private:
 
     // EventTarget
     EventTargetInterface eventTargetInterface() const final { return WebGPUDeviceEventTargetInterfaceType; }
-    ScriptExecutionContext* scriptExecutionContext() const final { return &m_scriptExecutionContext; }
     void refEventTarget() final { ref(); }
     void derefEventTarget() final { deref(); }
 
index cff4a98..c18a7aa 100644 (file)
@@ -29,7 +29,8 @@ typedef sequence<any> GPUMappedBuffer;  // [GPUBuffer, ArrayBuffer]
 [
     Conditional=WEBGPU,
     EnabledAtRuntime=WebGPU,
-    InterfaceName=GPUDevice
+    InterfaceName=GPUDevice,
+    JSGenerateToJSObject,
 ] interface WebGPUDevice : EventTarget {
     readonly attribute WebGPUAdapter adapter;
 
index 29be2a0..b451091 100644 (file)
@@ -33,6 +33,7 @@
 #include "CanvasRenderingContext.h"
 #include "CanvasRenderingContext2D.h"
 #include "Document.h"
+#include "Element.h"
 #include "FloatPoint.h"
 #include "Gradient.h"
 #include "HTMLCanvasElement.h"
 #include "JSCanvasFillRule.h"
 #include "JSCanvasLineCap.h"
 #include "JSCanvasLineJoin.h"
+#include "JSCanvasRenderingContext2D.h"
 #include "JSCanvasTextAlign.h"
 #include "JSCanvasTextBaseline.h"
 #include "JSExecState.h"
+#include "JSImageBitmapRenderingContext.h"
 #include "JSImageSmoothingQuality.h"
 #include "Path2D.h"
 #include "Pattern.h"
 #include "RecordingSwizzleTypes.h"
 #include "SVGPathUtilities.h"
 #include "StringAdaptors.h"
+#include <JavaScriptCore/IdentifiersFactory.h>
+#include <JavaScriptCore/ScriptCallStackFactory.h>
+#include <wtf/Function.h>
+
 #if ENABLE(CSS_TYPED_OM)
 #include "TypedOMCSSImageValue.h"
 #endif
+
 #if ENABLE(WEBGL)
+#include "JSWebGLRenderingContext.h"
 #include "WebGLRenderingContext.h"
 #endif
+
 #if ENABLE(WEBGL2)
+#include "JSWebGL2RenderingContext.h"
 #include "WebGL2RenderingContext.h"
 #endif
+
 #if ENABLE(WEBGPU)
 #include "GPUCanvasContext.h"
+#include "JSWebGPUDevice.h"
+#include "WebGPUDevice.h"
 #endif
-#include <JavaScriptCore/IdentifiersFactory.h>
-#include <JavaScriptCore/ScriptCallStackFactory.h>
-#include <wtf/Function.h>
 
 namespace WebCore {
 
 using namespace Inspector;
 
+#if ENABLE(WEBGPU)
+static HTMLCanvasElement* canvasIfContextMatchesDevice(CanvasRenderingContext& context, WebGPUDevice& device)
+{
+    if (is<GPUCanvasContext>(context)) {
+        auto& contextGPU = downcast<GPUCanvasContext>(context);
+        if (auto* webGPUSwapChain = contextGPU.swapChain()) {
+            if (auto* gpuSwapChain = webGPUSwapChain->swapChain()) {
+                if (gpuSwapChain == device.device().swapChain()) {
+                    if (is<HTMLCanvasElement>(contextGPU.canvasBase()))
+                        return &downcast<HTMLCanvasElement>(contextGPU.canvasBase());
+                }
+            }
+        }
+    }
+    return nullptr;
+}
+#endif
+
 Ref<InspectorCanvas> InspectorCanvas::create(CanvasRenderingContext& context)
 {
     return adoptRef(*new InspectorCanvas(context));
 }
 
+#if ENABLE(WEBGPU)
+Ref<InspectorCanvas> InspectorCanvas::create(WebGPUDevice& device)
+{
+    return adoptRef(*new InspectorCanvas(device));
+}
+#endif
+
 InspectorCanvas::InspectorCanvas(CanvasRenderingContext& context)
     : m_identifier("canvas:" + IdentifiersFactory::createIdentifier())
     , m_context(context)
 {
+#if ENABLE(WEBGPU)
+    // The actual "context" for WebGPU is the `WebGPUDevice`, not the <canvas>.
+    ASSERT(!is<GPUCanvasContext>(context));
+#endif
+}
+
+#if ENABLE(WEBGPU)
+InspectorCanvas::InspectorCanvas(WebGPUDevice& device)
+    : m_identifier("canvas:" + IdentifiersFactory::createIdentifier())
+    , m_context(device)
+{
+}
+#endif
+
+CanvasRenderingContext* InspectorCanvas::canvasContext() const
+{
+    if (auto* contextWrapper = WTF::get_if<std::reference_wrapper<CanvasRenderingContext>>(m_context))
+        return &contextWrapper->get();
+    return nullptr;
 }
 
-HTMLCanvasElement* InspectorCanvas::canvasElement()
+HTMLCanvasElement* InspectorCanvas::canvasElement() const
 {
-    if (is<HTMLCanvasElement>(m_context.canvasBase()))
-        return &downcast<HTMLCanvasElement>(m_context.canvasBase());
+    return WTF::switchOn(m_context,
+        [] (std::reference_wrapper<CanvasRenderingContext> contextWrapper) -> HTMLCanvasElement* {
+            auto& context = contextWrapper.get();
+            if (is<HTMLCanvasElement>(context.canvasBase()))
+                return &downcast<HTMLCanvasElement>(context.canvasBase());
+            return nullptr;
+        },
+#if ENABLE(WEBGPU)
+        [&] (std::reference_wrapper<WebGPUDevice> deviceWrapper) -> HTMLCanvasElement* {
+            auto& device = deviceWrapper.get();
+            {
+                LockHolder lock(CanvasRenderingContext::instancesMutex());
+                for (auto* canvasRenderingContext : CanvasRenderingContext::instances(lock)) {
+                    if (auto* canvasElement = canvasIfContextMatchesDevice(*canvasRenderingContext, device))
+                        return canvasElement;
+                }
+            }
+            return nullptr;
+        },
+#endif
+        [] (Monostate) {
+            ASSERT_NOT_REACHED();
+            return nullptr;
+        }
+    );
     return nullptr;
 }
 
+#if ENABLE(WEBGPU)
+WebGPUDevice* InspectorCanvas::deviceContext() const
+{
+    if (auto* deviceWrapper = WTF::get_if<std::reference_wrapper<WebGPUDevice>>(m_context))
+        return &deviceWrapper->get();
+    return nullptr;
+}
+
+bool InspectorCanvas::isDeviceForCanvasContext(CanvasRenderingContext& context) const
+{
+    if (auto* device = deviceContext())
+        return canvasIfContextMatchesDevice(context, *device);
+    return false;
+}
+#endif
+
+ScriptExecutionContext* InspectorCanvas::scriptExecutionContext() const
+{
+    return WTF::switchOn(m_context,
+        [] (std::reference_wrapper<CanvasRenderingContext> contextWrapper) {
+            auto& context = contextWrapper.get();
+            return context.canvasBase().scriptExecutionContext();
+        },
+#if ENABLE(WEBGPU)
+        [] (std::reference_wrapper<WebGPUDevice> deviceWrapper) {
+            auto& device = deviceWrapper.get();
+            return device.scriptExecutionContext();
+        },
+#endif
+        [] (Monostate) {
+            ASSERT_NOT_REACHED();
+            return nullptr;
+        }
+    );
+}
+
+JSC::JSValue InspectorCanvas::resolveContext(ExecState* exec) const
+{
+    JSC::JSLockHolder lock(exec);
+
+    auto* globalObject = deprecatedGlobalObjectForPrototype(exec);
+
+    return WTF::switchOn(m_context,
+        [&] (std::reference_wrapper<CanvasRenderingContext> contextWrapper) {
+            auto& context = contextWrapper.get();
+            if (is<CanvasRenderingContext2D>(context))
+                return toJS(exec, globalObject, downcast<CanvasRenderingContext2D>(context));
+            if (is<ImageBitmapRenderingContext>(context))
+                return toJS(exec, globalObject, downcast<ImageBitmapRenderingContext>(context));
+#if ENABLE(WEBGL)
+            if (is<WebGLRenderingContext>(context))
+                return toJS(exec, globalObject, downcast<WebGLRenderingContext>(context));
+#endif
+#if ENABLE(WEBGL2)
+            if (is<WebGL2RenderingContext>(context))
+                return toJS(exec, globalObject, downcast<WebGL2RenderingContext>(context));
+#endif
+            return JSC::JSValue();
+        },
+#if ENABLE(WEBGPU)
+        [&] (std::reference_wrapper<WebGPUDevice> deviceWrapper) {
+            return toJS(exec, globalObject, deviceWrapper.get());
+        },
+#endif
+        [] (Monostate) {
+            ASSERT_NOT_REACHED();
+            return JSC::JSValue();
+        }
+    );
+}
+
+HashSet<Element*> InspectorCanvas::clientNodes() const
+{
+    return WTF::switchOn(m_context,
+        [] (std::reference_wrapper<CanvasRenderingContext> contextWrapper) {
+            auto& context = contextWrapper.get();
+            return context.canvasBase().cssCanvasClients();
+        },
+#if ENABLE(WEBGPU)
+        [&] (std::reference_wrapper<WebGPUDevice> deviceWrapper) {
+            auto& device = deviceWrapper.get();
+
+            HashSet<Element*> canvasElementClients;
+            {
+                LockHolder lock(CanvasRenderingContext::instancesMutex());
+                for (auto* canvasRenderingContext : CanvasRenderingContext::instances(lock)) {
+                    if (auto* canvasElement = canvasIfContextMatchesDevice(*canvasRenderingContext, device))
+                        canvasElementClients.add(canvasElement);
+                }
+            }
+            return canvasElementClients;
+        },
+#endif
+        [] (Monostate) {
+            ASSERT_NOT_REACHED();
+            return HashSet<Element*>();
+        }
+    );
+}
+
 void InspectorCanvas::canvasChanged()
 {
-    if (!m_context.callTracingActive())
+    auto* context = canvasContext();
+    ASSERT(context);
+
+    if (!context->callTracingActive())
         return;
 
     // Since 2D contexts are able to be fully reproduced in the frontend, we don't need snapshots.
-    if (is<CanvasRenderingContext2D>(m_context))
+    if (is<CanvasRenderingContext2D>(context))
         return;
 
     m_contentChanged = true;
@@ -121,7 +302,11 @@ void InspectorCanvas::resetRecordingData()
     m_framesCaptured = 0;
     m_contentChanged = false;
 
-    m_context.setCallTracingActive(false);
+    auto* context = canvasContext();
+    ASSERT(context);
+    // FIXME: <https://webkit.org/b/201651> Web Inspector: Canvas: support canvas recordings for WebGPUDevice
+
+    context->setCallTracingActive(false);
 }
 
 bool InspectorCanvas::hasRecordingData() const
@@ -191,14 +376,18 @@ void InspectorCanvas::recordAction(const String& name, std::initializer_list<Rec
     m_bufferUsed += m_lastRecordedAction->memoryCost();
     m_currentActions->addItem(m_lastRecordedAction.get());
 
-    if (is<ImageBitmapRenderingContext>(m_context) && shouldSnapshotBitmapRendererAction(name))
+    auto* context = canvasContext();
+    ASSERT(context);
+    // FIXME: <https://webkit.org/b/201651> Web Inspector: Canvas: support canvas recordings for WebGPUDevice
+
+    if (is<ImageBitmapRenderingContext>(context) && shouldSnapshotBitmapRendererAction(name))
         m_contentChanged = true;
 #if ENABLE(WEBGL)
-    else if (is<WebGLRenderingContext>(m_context) && shouldSnapshotWebGLAction(name))
+    else if (is<WebGLRenderingContext>(context) && shouldSnapshotWebGLAction(name))
         m_contentChanged = true;
 #endif
 #if ENABLE(WEBGL2)
-    else if (is<WebGL2RenderingContext>(m_context) && shouldSnapshotWebGL2Action(name))
+    else if (is<WebGL2RenderingContext>(context) && shouldSnapshotWebGL2Action(name))
         m_contentChanged = true;
 #endif
 }
@@ -250,31 +439,42 @@ bool InspectorCanvas::overFrameCount() const
 
 Ref<Inspector::Protocol::Canvas::Canvas> InspectorCanvas::buildObjectForCanvas(bool captureBacktrace)
 {
-    Inspector::Protocol::Canvas::ContextType contextType;
-    if (is<CanvasRenderingContext2D>(m_context))
-        contextType = Inspector::Protocol::Canvas::ContextType::Canvas2D;
-    else if (is<ImageBitmapRenderingContext>(m_context))
-        contextType = Inspector::Protocol::Canvas::ContextType::BitmapRenderer;
+    using ContextTypeType = Optional<Inspector::Protocol::Canvas::ContextType>;
+    auto contextType = WTF::switchOn(m_context,
+        [] (std::reference_wrapper<CanvasRenderingContext> contextWrapper) -> ContextTypeType {
+            auto& context = contextWrapper.get();
+            if (is<CanvasRenderingContext2D>(context))
+                return Inspector::Protocol::Canvas::ContextType::Canvas2D;
+            if (is<ImageBitmapRenderingContext>(context))
+                return Inspector::Protocol::Canvas::ContextType::BitmapRenderer;
 #if ENABLE(WEBGL)
-    else if (is<WebGLRenderingContext>(m_context))
-        contextType = Inspector::Protocol::Canvas::ContextType::WebGL;
+            if (is<WebGLRenderingContext>(context))
+                return Inspector::Protocol::Canvas::ContextType::WebGL;
 #endif
 #if ENABLE(WEBGL2)
-    else if (is<WebGL2RenderingContext>(m_context))
-        contextType = Inspector::Protocol::Canvas::ContextType::WebGL2;
+            if (is<WebGL2RenderingContext>(context))
+                return Inspector::Protocol::Canvas::ContextType::WebGL2;
 #endif
+            return WTF::nullopt;
+        },
 #if ENABLE(WEBGPU)
-    else if (is<GPUCanvasContext>(m_context))
-        contextType = Inspector::Protocol::Canvas::ContextType::WebGPU;
+        [] (std::reference_wrapper<WebGPUDevice>) {
+            return Inspector::Protocol::Canvas::ContextType::WebGPU;
+        },
 #endif
-    else {
+        [] (Monostate) {
+            ASSERT_NOT_REACHED();
+            return WTF::nullopt;
+        }
+    );
+    if (!contextType) {
         ASSERT_NOT_REACHED();
         contextType = Inspector::Protocol::Canvas::ContextType::Canvas2D;
     }
 
     auto canvas = Inspector::Protocol::Canvas::Canvas::create()
         .setCanvasId(m_identifier)
-        .setContextType(contextType)
+        .setContextType(contextType.value())
         .release();
 
     if (auto* node = canvasElement()) {
@@ -285,28 +485,75 @@ Ref<Inspector::Protocol::Canvas::Canvas> InspectorCanvas::buildObjectForCanvas(b
         // FIXME: <https://webkit.org/b/178282> Web Inspector: send a DOM node with each Canvas payload and eliminate Canvas.requestNode
     }
 
-    if (is<ImageBitmapRenderingContext>(m_context)) {
-        auto contextAttributes = Inspector::Protocol::Canvas::ContextAttributes::create()
-            .release();
-        contextAttributes->setAlpha(downcast<ImageBitmapRenderingContext>(m_context).hasAlpha());
-        canvas->setContextAttributes(WTFMove(contextAttributes));
-    }
+    using ContextAttributesType = RefPtr<Inspector::Protocol::Canvas::ContextAttributes>;
+    auto contextAttributes = WTF::switchOn(m_context,
+        [] (std::reference_wrapper<CanvasRenderingContext> contextWrapper) -> ContextAttributesType {
+            auto& context = contextWrapper.get();
+            if (is<ImageBitmapRenderingContext>(context)) {
+                auto contextAttributesPayload = Inspector::Protocol::Canvas::ContextAttributes::create()
+                    .release();
+                contextAttributesPayload->setAlpha(downcast<ImageBitmapRenderingContext>(context).hasAlpha());
+                return WTFMove(contextAttributesPayload);
+            }
+
 #if ENABLE(WEBGL)
-    else if (is<WebGLRenderingContextBase>(m_context)) {
-        if (Optional<WebGLContextAttributes> attributes = downcast<WebGLRenderingContextBase>(m_context).getContextAttributes()) {
-            auto contextAttributes = Inspector::Protocol::Canvas::ContextAttributes::create()
-                .release();
-            contextAttributes->setAlpha(attributes->alpha);
-            contextAttributes->setDepth(attributes->depth);
-            contextAttributes->setStencil(attributes->stencil);
-            contextAttributes->setAntialias(attributes->antialias);
-            contextAttributes->setPremultipliedAlpha(attributes->premultipliedAlpha);
-            contextAttributes->setPreserveDrawingBuffer(attributes->preserveDrawingBuffer);
-            contextAttributes->setFailIfMajorPerformanceCaveat(attributes->failIfMajorPerformanceCaveat);
-            canvas->setContextAttributes(WTFMove(contextAttributes));
-        }
-    }
+            if (is<WebGLRenderingContextBase>(context)) {
+                if (const auto& attributes = downcast<WebGLRenderingContextBase>(context).getContextAttributes()) {
+                    auto contextAttributesPayload = Inspector::Protocol::Canvas::ContextAttributes::create()
+                        .release();
+                    contextAttributesPayload->setAlpha(attributes->alpha);
+                    contextAttributesPayload->setDepth(attributes->depth);
+                    contextAttributesPayload->setStencil(attributes->stencil);
+                    contextAttributesPayload->setAntialias(attributes->antialias);
+                    contextAttributesPayload->setPremultipliedAlpha(attributes->premultipliedAlpha);
+                    contextAttributesPayload->setPreserveDrawingBuffer(attributes->preserveDrawingBuffer);
+                    switch (attributes->powerPreference) {
+                    case WebGLPowerPreference::Default:
+                        contextAttributesPayload->setPowerPreference("default");
+                        break;
+                    case WebGLPowerPreference::LowPower:
+                        contextAttributesPayload->setPowerPreference("low-power");
+                        break;
+                    case WebGLPowerPreference::HighPerformance:
+                        contextAttributesPayload->setPowerPreference("high-performance");
+                        break;
+                    }
+                    contextAttributesPayload->setFailIfMajorPerformanceCaveat(attributes->failIfMajorPerformanceCaveat);
+                    return WTFMove(contextAttributesPayload);
+                }
+            }
 #endif
+            return nullptr;
+        },
+#if ENABLE(WEBGPU)
+        [] (std::reference_wrapper<WebGPUDevice> deviceWrapper) -> ContextAttributesType {
+            auto& device = deviceWrapper.get();
+            if (const auto& options = device.adapter().options()) {
+                auto contextAttributesPayload = Inspector::Protocol::Canvas::ContextAttributes::create()
+                    .release();
+                if (const auto& powerPreference = options->powerPreference) {
+                    switch (powerPreference.value()) {
+                    case GPUPowerPreference::LowPower:
+                        contextAttributesPayload->setPowerPreference("low-power");
+                        break;
+
+                    case GPUPowerPreference::HighPerformance:
+                        contextAttributesPayload->setPowerPreference("high-performance");
+                        break;
+                    }
+                }
+                return WTFMove(contextAttributesPayload);
+            }
+            return nullptr;
+        },
+#endif
+        [] (Monostate) {
+            ASSERT_NOT_REACHED();
+            return nullptr;
+        }
+    );
+    if (contextAttributes)
+        canvas->setContextAttributes(WTFMove(contextAttributes));
 
     // FIXME: <https://webkit.org/b/180833> Web Inspector: support OffscreenCanvas for Canvas related operations
 
@@ -329,17 +576,21 @@ Ref<Inspector::Protocol::Recording::Recording> InspectorCanvas::releaseObjectFor
     ASSERT(!m_lastRecordedAction);
     ASSERT(!m_frames);
 
+    auto* context = canvasContext();
+    ASSERT(context);
+    // FIXME: <https://webkit.org/b/201651> Web Inspector: Canvas: support canvas recordings for WebGPUDevice
+
     Inspector::Protocol::Recording::Type type;
-    if (is<CanvasRenderingContext2D>(m_context))
+    if (is<CanvasRenderingContext2D>(context))
         type = Inspector::Protocol::Recording::Type::Canvas2D;
-    else if (is<ImageBitmapRenderingContext>(m_context))
+    else if (is<ImageBitmapRenderingContext>(context))
         type = Inspector::Protocol::Recording::Type::CanvasBitmapRenderer;
 #if ENABLE(WEBGL)
-    else if (is<WebGLRenderingContext>(m_context))
+    else if (is<WebGLRenderingContext>(context))
         type = Inspector::Protocol::Recording::Type::CanvasWebGL;
 #endif
 #if ENABLE(WEBGL2)
-    else if (is<WebGL2RenderingContext>(m_context))
+    else if (is<WebGL2RenderingContext>(context))
         type = Inspector::Protocol::Recording::Type::CanvasWebGL2;
 #endif
     else {
@@ -364,33 +615,23 @@ Ref<Inspector::Protocol::Recording::Recording> InspectorCanvas::releaseObjectFor
 
 String InspectorCanvas::getCanvasContentAsDataURL(ErrorString& errorString)
 {
-    // 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) {
-        errorString = "Context isn't related to an HTMLCanvasElement"_s;
+        errorString = "Missing HTMLCanvasElement of canvas for given canvasId"_s;
         return emptyString();
     }
 
 #if ENABLE(WEBGL)
-    if (is<WebGLRenderingContextBase>(m_context))
-        downcast<WebGLRenderingContextBase>(m_context).setPreventBufferClearForInspector(true);
+    auto* context = node->renderingContext();
+    if (is<WebGLRenderingContextBase>(context))
+        downcast<WebGLRenderingContextBase>(*context).setPreventBufferClearForInspector(true);
 #endif
 
     ExceptionOr<UncachedString> result = node->toDataURL("image/png"_s);
 
 #if ENABLE(WEBGL)
-    if (is<WebGLRenderingContextBase>(m_context))
-        downcast<WebGLRenderingContextBase>(m_context).setPreventBufferClearForInspector(false);
+    if (is<WebGLRenderingContextBase>(context))
+        downcast<WebGLRenderingContextBase>(*context).setPreventBufferClearForInspector(false);
 #endif
 
     if (result.hasException()) {
@@ -558,18 +799,22 @@ template<typename T> static Ref<JSON::ArrayOf<JSON::Value>> buildArrayForVector(
 
 Ref<Inspector::Protocol::Recording::InitialState> InspectorCanvas::buildInitialState()
 {
+    auto* context = canvasContext();
+    ASSERT(context);
+    // FIXME: <https://webkit.org/b/201651> Web Inspector: Canvas: support canvas recordings for WebGPUDevice
+
     auto initialStatePayload = Inspector::Protocol::Recording::InitialState::create().release();
 
     auto attributesPayload = JSON::Object::create();
-    attributesPayload->setInteger("width"_s, m_context.canvasBase().width());
-    attributesPayload->setInteger("height"_s, m_context.canvasBase().height());
+    attributesPayload->setInteger("width"_s, context->canvasBase().width());
+    attributesPayload->setInteger("height"_s, context->canvasBase().height());
 
     auto statesPayload = JSON::ArrayOf<JSON::Object>::create();
 
     auto parametersPayload = JSON::ArrayOf<JSON::Value>::create();
 
-    if (is<CanvasRenderingContext2D>(m_context)) {
-        auto& context2d = downcast<CanvasRenderingContext2D>(m_context);
+    if (is<CanvasRenderingContext2D>(context)) {
+        auto& context2d = downcast<CanvasRenderingContext2D>(*context);
         for (auto& state : context2d.stateStack()) {
             auto statePayload = JSON::Object::create();
 
@@ -626,8 +871,8 @@ Ref<Inspector::Protocol::Recording::InitialState> InspectorCanvas::buildInitialS
         }
     }
 #if ENABLE(WEBGL)
-    else if (is<WebGLRenderingContextBase>(m_context)) {
-        WebGLRenderingContextBase& contextWebGLBase = downcast<WebGLRenderingContextBase>(m_context);
+    else if (is<WebGLRenderingContextBase>(context)) {
+        auto& contextWebGLBase = downcast<WebGLRenderingContextBase>(*context);
         if (Optional<WebGLContextAttributes> webGLContextAttributes = contextWebGLBase.getContextAttributes()) {
             auto webGLContextAttributesPayload = JSON::Object::create();
             webGLContextAttributesPayload->setBoolean("alpha"_s, webGLContextAttributes->alpha);
index 38542fb..dad952d 100644 (file)
 #pragma once
 
 #include "CallTracerTypes.h"
+#include "CanvasRenderingContext.h"
 #include <JavaScriptCore/InspectorProtocolObjects.h>
+#include <JavaScriptCore/JSCInlines.h>
 #include <JavaScriptCore/ScriptCallFrame.h>
 #include <JavaScriptCore/ScriptCallStack.h>
 #include <initializer_list>
+#include <wtf/HashSet.h>
 #include <wtf/Variant.h>
 #include <wtf/Vector.h>
 #include <wtf/text/WTFString.h>
 
+#if ENABLE(WEBGPU)
+#include "WebGPUDevice.h"
+#endif
+
 namespace WebCore {
 
 class CanvasGradient;
 class CanvasPattern;
-class CanvasRenderingContext;
+class Element;
 class HTMLCanvasElement;
 class HTMLImageElement;
 class HTMLVideoElement;
@@ -53,11 +60,25 @@ typedef String ErrorString;
 class InspectorCanvas final : public RefCounted<InspectorCanvas> {
 public:
     static Ref<InspectorCanvas> create(CanvasRenderingContext&);
+#if ENABLE(WEBGPU)
+    static Ref<InspectorCanvas> create(WebGPUDevice&);
+#endif
+
+    const String& identifier() const { return m_identifier; }
+
+    CanvasRenderingContext* canvasContext() const;
+    HTMLCanvasElement* canvasElement() const;
+
+#if ENABLE(WEBGPU)
+    WebGPUDevice* deviceContext() const;
+    bool isDeviceForCanvasContext(CanvasRenderingContext&) const;
+#endif
+
+    ScriptExecutionContext* scriptExecutionContext() const;
 
-    const String& identifier() { return m_identifier; }
-    CanvasRenderingContext& context() { return m_context; }
+    JSC::JSValue resolveContext(JSC::ExecState*) const;
 
-    HTMLCanvasElement* canvasElement();
+    HashSet<Element*> clientNodes() const;
 
     void canvasChanged();
 
@@ -87,6 +108,10 @@ public:
 
 private:
     InspectorCanvas(CanvasRenderingContext&);
+#if ENABLE(WEBGPU)
+    InspectorCanvas(WebGPUDevice&);
+#endif
+
     void appendActionSnapshotIfNeeded();
 
     using DuplicateDataVariant = Variant<
@@ -116,7 +141,14 @@ private:
     Ref<JSON::ArrayOf<JSON::Value>> buildArrayForImageData(const ImageData&);
 
     String m_identifier;
-    CanvasRenderingContext& m_context;
+
+    Variant<
+        std::reference_wrapper<CanvasRenderingContext>,
+#if ENABLE(WEBGPU)
+        std::reference_wrapper<WebGPUDevice>,
+#endif
+        Monostate
+    > m_context;
 
     RefPtr<Inspector::Protocol::Recording::InitialState> m_initialState;
     RefPtr<JSON::ArrayOf<Inspector::Protocol::Recording::Frame>> m_frames;
index e279fe8..6964650 100644 (file)
 #include <JavaScriptCore/ScriptCallStack.h>
 #include <wtf/StdLibExtras.h>
 
+#if ENABLE(WEBGPU)
+#include "WebGPUSwapChain.h"
+#endif
+
 namespace WebCore {
 
 using namespace Inspector;
@@ -1102,6 +1106,26 @@ bool InspectorInstrumentation::isShaderProgramHighlightedImpl(InstrumentingAgent
 }
 #endif
 
+#if ENABLE(WEBGPU)
+void InspectorInstrumentation::didCreateWebGPUDeviceImpl(InstrumentingAgents& instrumentingAgents, WebGPUDevice& device)
+{
+    if (auto* canvasAgent = instrumentingAgents.inspectorCanvasAgent())
+        canvasAgent->didCreateWebGPUDevice(device);
+}
+
+void InspectorInstrumentation::willDestroyWebGPUDeviceImpl(InstrumentingAgents& instrumentingAgents, WebGPUDevice& device)
+{
+    if (auto* canvasAgent = instrumentingAgents.inspectorCanvasAgent())
+        canvasAgent->willDestroyWebGPUDevice(device);
+}
+
+void InspectorInstrumentation::willConfigureSwapChainImpl(InstrumentingAgents& instrumentingAgents, GPUCanvasContext& contextGPU, WebGPUSwapChain& newSwapChain)
+{
+    if (auto* canvasAgent = instrumentingAgents.inspectorCanvasAgent())
+        canvasAgent->willConfigureSwapChain(contextGPU, newSwapChain);
+}
+#endif
+
 #if ENABLE(RESOURCE_USAGE)
 void InspectorInstrumentation::didHandleMemoryPressureImpl(InstrumentingAgents& instrumentingAgents, Critical critical)
 {
index b44f930..81f3cdc 100644 (file)
 #include "WebGLRenderingContextBase.h"
 #endif
 
+#if ENABLE(WEBGPU)
+#include "GPUCanvasContext.h"
+#include "WebGPUDevice.h"
+#endif
+
 namespace Inspector {
 class ConsoleMessage;
 class ScriptArguments;
@@ -92,11 +97,16 @@ class SecurityOrigin;
 class ShadowRoot;
 class SharedBuffer;
 class TimerBase;
+class WebKitNamedFlow;
+class WorkerInspectorProxy;
+
 #if ENABLE(WEBGL)
 class WebGLProgram;
 #endif
-class WebKitNamedFlow;
-class WorkerInspectorProxy;
+
+#if ENABLE(WEBGPU)
+class WebGPUSwapChain;
+#endif
 
 enum class StorageType;
 
@@ -284,6 +294,11 @@ public:
     static bool isShaderProgramDisabled(WebGLRenderingContextBase&, WebGLProgram&);
     static bool isShaderProgramHighlighted(WebGLRenderingContextBase&, WebGLProgram&);
 #endif
+#if ENABLE(WEBGPU)
+    static void didCreateWebGPUDevice(WebGPUDevice&);
+    static void willDestroyWebGPUDevice(WebGPUDevice&);
+    static void willConfigureSwapChain(GPUCanvasContext&, WebGPUSwapChain&);
+#endif
 
     static void networkStateChanged(Page&);
     static void updateApplicationCacheStatus(Frame*);
@@ -471,6 +486,11 @@ private:
     static bool isShaderProgramDisabledImpl(InstrumentingAgents&, WebGLProgram&);
     static bool isShaderProgramHighlightedImpl(InstrumentingAgents&, WebGLProgram&);
 #endif
+#if ENABLE(WEBGPU)
+    static void didCreateWebGPUDeviceImpl(InstrumentingAgents&, WebGPUDevice&);
+    static void willDestroyWebGPUDeviceImpl(InstrumentingAgents&, WebGPUDevice&);
+    static void willConfigureSwapChainImpl(InstrumentingAgents&, GPUCanvasContext&, WebGPUSwapChain&);
+#endif
 
     static void layerTreeDidChangeImpl(InstrumentingAgents&);
     static void renderLayerDestroyedImpl(InstrumentingAgents&, const RenderLayer&);
@@ -1395,6 +1415,29 @@ inline bool InspectorInstrumentation::isShaderProgramHighlighted(WebGLRenderingC
 }
 #endif
 
+#if ENABLE(WEBGPU)
+inline void InspectorInstrumentation::didCreateWebGPUDevice(WebGPUDevice& device)
+{
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    if (auto* instrumentingAgents = instrumentingAgentsForContext(device.scriptExecutionContext()))
+        didCreateWebGPUDeviceImpl(*instrumentingAgents, device);
+}
+
+inline void InspectorInstrumentation::willDestroyWebGPUDevice(WebGPUDevice& device)
+{
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    if (auto* instrumentingAgents = instrumentingAgentsForContext(device.scriptExecutionContext()))
+        willDestroyWebGPUDeviceImpl(*instrumentingAgents, device);
+}
+
+inline void InspectorInstrumentation::willConfigureSwapChain(GPUCanvasContext& contextGPU, WebGPUSwapChain& newSwapChain)
+{
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    if (auto* instrumentingAgents = instrumentingAgentsForContext(contextGPU.canvasBase().scriptExecutionContext()))
+        willConfigureSwapChainImpl(*instrumentingAgents, contextGPU, newSwapChain);
+}
+#endif
+
 inline void InspectorInstrumentation::networkStateChanged(Page& page)
 {
     FAST_RETURN_IF_NO_FRONTENDS(void());
index 05328ec..d529550 100644 (file)
@@ -55,8 +55,9 @@ InspectorShaderProgram::InspectorShaderProgram(WebGLProgram& program, InspectorC
 
 WebGLRenderingContextBase& InspectorShaderProgram::context() const
 {
-    ASSERT(is<WebGLRenderingContextBase>(m_canvas.context()));
-    return downcast<WebGLRenderingContextBase>(m_canvas.context());
+    ASSERT(m_canvas.canvasContext());
+    ASSERT(is<WebGLRenderingContextBase>(*m_canvas.canvasContext()));
+    return downcast<WebGLRenderingContextBase>(*m_canvas.canvasContext());
 }
 
 WebGLShader* InspectorShaderProgram::shaderForType(const String& protocolType)
index 88e2c09..1852248 100644 (file)
@@ -36,9 +36,7 @@
 #include "ImageBitmapRenderingContext.h"
 #include "InspectorDOMAgent.h"
 #include "InstrumentingAgents.h"
-#include "JSCanvasRenderingContext2D.h"
 #include "JSExecState.h"
-#include "JSImageBitmapRenderingContext.h"
 #include "Microtasks.h"
 #include "OffscreenCanvas.h"
 #include "ScriptState.h"
 #include <wtf/Lock.h>
 
 #if ENABLE(WEBGL)
-#include "JSWebGLRenderingContext.h"
 #include "WebGLProgram.h"
+#include "WebGLRenderingContext.h"
+#include "WebGLRenderingContextBase.h"
 #include "WebGLShader.h"
 #endif
 
 #if ENABLE(WEBGL2)
-#include "JSWebGL2RenderingContext.h"
+#include "WebGL2RenderingContext.h"
 #endif
 
 #if ENABLE(WEBGPU)
-#include "JSGPUCanvasContext.h"
+#include "GPUCanvasContext.h"
+#include "WebGPUDevice.h"
 #endif
 
 namespace WebCore {
@@ -103,11 +103,7 @@ void InspectorCanvasAgent::enable(ErrorString&)
 
     m_instrumentingAgents.setInspectorCanvasAgent(this);
 
-    const auto canvasExistsInCurrentPage = [&] (CanvasRenderingContext* canvasRenderingContext) {
-        if (!canvasRenderingContext)
-            return false;
-
-        auto* scriptExecutionContext = canvasRenderingContext->canvasBase().scriptExecutionContext();
+    const auto existsInCurrentPage = [&] (ScriptExecutionContext* scriptExecutionContext) {
         if (!is<Document>(scriptExecutionContext))
             return false;
 
@@ -118,18 +114,34 @@ void InspectorCanvasAgent::enable(ErrorString&)
 
     {
         LockHolder lock(CanvasRenderingContext::instancesMutex());
-        for (auto* canvasRenderingContext : CanvasRenderingContext::instances(lock)) {
-            if (canvasExistsInCurrentPage(canvasRenderingContext))
-                bindCanvas(*canvasRenderingContext, false);
+        for (auto* context : CanvasRenderingContext::instances(lock)) {
+#if ENABLE(WEBGPU)
+            // The actual "context" for WebGPU is the `WebGPUDevice`, not the <canvas>.
+            if (is<GPUCanvasContext>(context))
+                continue;
+#endif
+
+            if (existsInCurrentPage(context->canvasBase().scriptExecutionContext()))
+                bindCanvas(*context, false);
+        }
+    }
+
+#if ENABLE(WEBGPU)
+    {
+        LockHolder lock(WebGPUDevice::instancesMutex());
+        for (auto* device : WebGPUDevice::instances(lock)) {
+            if (existsInCurrentPage(device->scriptExecutionContext()))
+                bindCanvas(*device, false);
         }
     }
+#endif
 
 #if ENABLE(WEBGL)
     {
         LockHolder lock(WebGLProgram::instancesMutex());
-        for (auto& entry : WebGLProgram::instances(lock)) {
-            if (canvasExistsInCurrentPage(entry.value))
-                didCreateProgram(*entry.value, *entry.key);
+        for (auto& [program, contextWebGLBase] : WebGLProgram::instances(lock)) {
+            if (contextWebGLBase && existsInCurrentPage(contextWebGLBase->canvasBase().scriptExecutionContext()))
+                didCreateProgram(*contextWebGLBase, *program);
         }
     }
 #endif
@@ -174,54 +186,37 @@ void InspectorCanvasAgent::requestContent(ErrorString& errorString, const String
     *content = inspectorCanvas->getCanvasContentAsDataURL(errorString);
 }
 
-void InspectorCanvasAgent::requestCSSCanvasClientNodes(ErrorString& errorString, const String& canvasId, RefPtr<JSON::ArrayOf<int>>& result)
+void InspectorCanvasAgent::requestClientNodes(ErrorString& errorString, const String& canvasId, RefPtr<JSON::ArrayOf<int>>& clientNodeIds)
 {
+    auto* domAgent = m_instrumentingAgents.inspectorDOMAgent();
+    if (!domAgent) {
+        errorString = "DOM domain must be enabled"_s;
+        return;
+    }
+
     auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
     if (!inspectorCanvas)
         return;
 
-    result = JSON::ArrayOf<int>::create();
-    for (auto* client : inspectorCanvas->context().canvasBase().cssCanvasClients()) {
-        if (int documentNodeId = m_instrumentingAgents.inspectorDOMAgent()->boundNodeId(&client->document()))
-            result->addItem(m_instrumentingAgents.inspectorDOMAgent()->pushNodeToFrontend(errorString, documentNodeId, client));
+    clientNodeIds = JSON::ArrayOf<int>::create();
+    for (auto& clientNode : inspectorCanvas->clientNodes()) {
+        if (auto documentNodeId = domAgent->boundNodeId(&clientNode->document()))
+            clientNodeIds->addItem(domAgent->pushNodeToFrontend(errorString, documentNodeId, clientNode));
     }
 }
 
-static JSC::JSValue contextAsScriptValue(JSC::ExecState& state, CanvasRenderingContext& context)
-{
-    JSC::JSLockHolder lock(&state);
-
-    if (is<CanvasRenderingContext2D>(context))
-        return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<CanvasRenderingContext2D>(context));
-#if ENABLE(WEBGL)
-    if (is<WebGLRenderingContext>(context))
-        return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<WebGLRenderingContext>(context));
-#endif
-#if ENABLE(WEBGL2)
-    if (is<WebGL2RenderingContext>(context))
-        return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<WebGL2RenderingContext>(context));
-#endif
-#if ENABLE(WEBGPU)
-    if (is<GPUCanvasContext>(context))
-        return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<GPUCanvasContext>(context));
-#endif
-    if (is<ImageBitmapRenderingContext>(context))
-        return toJS(&state, deprecatedGlobalObjectForPrototype(&state), downcast<ImageBitmapRenderingContext>(context));
-
-    return { };
-}
-
-void InspectorCanvasAgent::resolveCanvasContext(ErrorString& errorString, const String& canvasId, const String* objectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result)
+void InspectorCanvasAgent::resolveContext(ErrorString& errorString, const String& canvasId, const String* objectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result)
 {
     auto inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
     if (!inspectorCanvas)
         return;
 
-    auto& state = *inspectorCanvas->context().canvasBase().scriptExecutionContext()->execState();
-    auto injectedScript = m_injectedScriptManager.injectedScriptFor(&state);
+    auto* state = inspectorCanvas->scriptExecutionContext()->execState();
+    auto injectedScript = m_injectedScriptManager.injectedScriptFor(state);
     ASSERT(!injectedScript.hasNoValue());
 
-    JSC::JSValue value = contextAsScriptValue(state, inspectorCanvas->context());
+    JSC::JSValue value = inspectorCanvas->resolveContext(state);
+
     if (!value) {
         ASSERT_NOT_REACHED();
         errorString = "Internal error: unknown context of canvas for given canvasId"_s;
@@ -246,7 +241,13 @@ void InspectorCanvasAgent::startRecording(ErrorString& errorString, const String
     if (!inspectorCanvas)
         return;
 
-    if (inspectorCanvas->context().callTracingActive()) {
+    // FIXME: <https://webkit.org/b/201651> Web Inspector: Canvas: support canvas recordings for WebGPUDevice
+
+    auto* context = inspectorCanvas->canvasContext();
+    if (!context)
+        return;
+
+    if (context->callTracingActive()) {
         errorString = "Already recording canvas"_s;
         return;
     }
@@ -265,12 +266,18 @@ void InspectorCanvasAgent::stopRecording(ErrorString& errorString, const String&
     if (!inspectorCanvas)
         return;
 
-    if (!inspectorCanvas->context().callTracingActive()) {
+    // FIXME: <https://webkit.org/b/201651> Web Inspector: Canvas: support canvas recordings for WebGPUDevice
+
+    auto* context = inspectorCanvas->canvasContext();
+    if (!context)
+        return;
+
+    if (!context->callTracingActive()) {
         errorString = "Not recording canvas"_s;
         return;
     }
 
-    didFinishRecordingCanvasFrame(inspectorCanvas->context(), true);
+    didFinishRecordingCanvasFrame(*context, true);
 }
 
 void InspectorCanvasAgent::requestShaderSource(ErrorString& errorString, const String& programId, const String& shaderType, String* content)
@@ -371,10 +378,8 @@ void InspectorCanvasAgent::frameNavigated(Frame& frame)
         }
     }
 
-    for (auto* inspectorCanvas : inspectorCanvases) {
-        String identifier = unbindCanvas(*inspectorCanvas);
-        m_frontendDispatcher->canvasRemoved(identifier);
-    }
+    for (auto* inspectorCanvas : inspectorCanvases)
+        unbindCanvas(*inspectorCanvas);
 }
 
 void InspectorCanvasAgent::didChangeCSSCanvasClientNodes(CanvasBase& canvasBase)
@@ -390,7 +395,7 @@ void InspectorCanvasAgent::didChangeCSSCanvasClientNodes(CanvasBase& canvasBase)
     if (!inspectorCanvas)
         return;
 
-    m_frontendDispatcher->cssCanvasClientNodesChanged(inspectorCanvas->identifier());
+    m_frontendDispatcher->clientNodesChanged(inspectorCanvas->identifier());
 }
 
 void InspectorCanvasAgent::didCreateCanvasRenderingContext(CanvasRenderingContext& context)
@@ -411,7 +416,22 @@ void InspectorCanvasAgent::didCreateCanvasRenderingContext(CanvasRenderingContex
 
 void InspectorCanvasAgent::didChangeCanvasMemory(CanvasRenderingContext& context)
 {
-    auto inspectorCanvas = findInspectorCanvas(context);
+    RefPtr<InspectorCanvas> inspectorCanvas;
+
+#if ENABLE(WEBGPU)
+    if (is<GPUCanvasContext>(context)) {
+        for (auto& item : m_identifierToInspectorCanvas.values()) {
+            if (item->isDeviceForCanvasContext(context)) {
+                inspectorCanvas = item;
+                break;
+            }
+        }
+    }
+#endif
+
+    if (!inspectorCanvas)
+        inspectorCanvas = findInspectorCanvas(context);
+
     ASSERT(inspectorCanvas);
     if (!inspectorCanvas)
         return;
@@ -436,7 +456,7 @@ void InspectorCanvasAgent::recordCanvasAction(CanvasRenderingContext& canvasRend
     // 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()) {
+        if (auto* scriptExecutionContext = inspectorCanvas->scriptExecutionContext()) {
             auto& queue = MicrotaskQueue::mainThreadQueue();
             queue.append(makeUnique<ActiveDOMCallbackMicrotask>(queue, *scriptExecutionContext, [&, protectedInspectorCanvas = inspectorCanvas.copyRef()] {
                 if (auto* canvasElement = protectedInspectorCanvas->canvasElement()) {
@@ -444,8 +464,8 @@ void InspectorCanvasAgent::recordCanvasAction(CanvasRenderingContext& canvasRend
                         return;
                 }
 
-                if (protectedInspectorCanvas->context().callTracingActive())
-                    didFinishRecordingCanvasFrame(protectedInspectorCanvas->context());
+                if (canvasRenderingContext.callTracingActive())
+                    didFinishRecordingCanvasFrame(canvasRenderingContext);
             }));
         }
     }
@@ -453,7 +473,7 @@ void InspectorCanvasAgent::recordCanvasAction(CanvasRenderingContext& canvasRend
     inspectorCanvas->recordAction(name, WTFMove(parameters));
 
     if (!inspectorCanvas->hasBufferSpace())
-        didFinishRecordingCanvasFrame(inspectorCanvas->context(), true);
+        didFinishRecordingCanvasFrame(canvasRenderingContext, true);
 }
 
 void InspectorCanvasAgent::canvasChanged(CanvasBase& canvasBase, const FloatRect&)
@@ -481,27 +501,19 @@ void InspectorCanvasAgent::canvasDestroyed(CanvasBase& canvasBase)
     if (!inspectorCanvas)
         return;
 
-    String identifier = unbindCanvas(*inspectorCanvas);
-
-    // WebCore::CanvasObserver::canvasDestroyed is called in response to the GC destroying the CanvasBase.
-    // Due to the single-process model used in WebKit1, the event must be dispatched from a timer to prevent
-    // the frontend from making JS allocations while the GC is still active.
-    m_removedCanvasIdentifiers.append(identifier);
-
-    if (!m_canvasDestroyedTimer.isActive())
-        m_canvasDestroyedTimer.startOneShot(0_s);
+    unbindCanvas(*inspectorCanvas);
 }
 
 void InspectorCanvasAgent::didFinishRecordingCanvasFrame(CanvasRenderingContext& context, bool forceDispatch)
 {
+    if (!context.callTracingActive())
+        return;
+
     auto inspectorCanvas = findInspectorCanvas(context);
     ASSERT(inspectorCanvas);
     if (!inspectorCanvas)
         return;
 
-    if (!inspectorCanvas->context().callTracingActive())
-        return;
-
     if (!inspectorCanvas->hasRecordingData()) {
         if (forceDispatch) {
             m_frontendDispatcher->recordingFinished(inspectorCanvas->identifier(), nullptr);
@@ -600,21 +612,63 @@ bool InspectorCanvasAgent::isShaderProgramHighlighted(WebGLProgram& program)
 }
 #endif
 
+#if ENABLE(WEBGPU)
+void InspectorCanvasAgent::didCreateWebGPUDevice(WebGPUDevice& device)
+{
+    if (findInspectorCanvas(device)) {
+        ASSERT_NOT_REACHED();
+        return;
+    }
+
+    bindCanvas(device, true);
+}
+
+void InspectorCanvasAgent::willDestroyWebGPUDevice(WebGPUDevice& device)
+{
+    auto inspectorCanvas = findInspectorCanvas(device);
+    ASSERT(inspectorCanvas);
+    if (!inspectorCanvas)
+        return;
+
+    unbindCanvas(*inspectorCanvas);
+}
+
+void InspectorCanvasAgent::willConfigureSwapChain(GPUCanvasContext& contextGPU, WebGPUSwapChain& newSwapChain)
+{
+    auto notifyDeviceForSwapChain = [&] (WebGPUSwapChain& webGPUSwapChain) {
+        for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
+            if (auto* device = inspectorCanvas->deviceContext()) {
+                if (device->device().swapChain() == webGPUSwapChain.swapChain())
+                    m_frontendDispatcher->clientNodesChanged(inspectorCanvas->identifier());
+            }
+        }
+    };
+
+    if (auto* existingSwapChain = contextGPU.swapChain())
+        notifyDeviceForSwapChain(*existingSwapChain);
+
+    notifyDeviceForSwapChain(newSwapChain);
+}
+#endif
+
 void InspectorCanvasAgent::startRecording(InspectorCanvas& inspectorCanvas, Inspector::Protocol::Recording::Initiator initiator, RecordingOptions&& recordingOptions)
 {
-    auto& canvasRenderingContext = inspectorCanvas.context();
+    auto* context = inspectorCanvas.canvasContext();
+    ASSERT(context);
+    // FIXME: <https://webkit.org/b/201651> Web Inspector: Canvas: support canvas recordings for WebGPUDevice
 
-    if (!is<CanvasRenderingContext2D>(canvasRenderingContext)
+    if (!is<CanvasRenderingContext2D>(context)
+        && !is<ImageBitmapRenderingContext>(context)
 #if ENABLE(WEBGL)
-        && !is<WebGLRenderingContext>(canvasRenderingContext)
+        && !is<WebGLRenderingContext>(context)
 #endif
 #if ENABLE(WEBGL2)
-        && !is<WebGL2RenderingContext>(canvasRenderingContext)
+        && !is<WebGL2RenderingContext>(context)
 #endif
-        && !is<ImageBitmapRenderingContext>(canvasRenderingContext))
+    )
         return;
 
-    if (canvasRenderingContext.callTracingActive())
+    if (context->callTracingActive())
         return;
 
     inspectorCanvas.resetRecordingData();
@@ -624,7 +678,7 @@ void InspectorCanvasAgent::startRecording(InspectorCanvas& inspectorCanvas, Insp
         inspectorCanvas.setBufferLimit(recordingOptions.memoryLimit.value());
     if (recordingOptions.name)
         inspectorCanvas.setRecordingName(recordingOptions.name.value());
-    canvasRenderingContext.setCallTracingActive(true);
+    context->setCallTracingActive(true);
 
     m_frontendDispatcher->recordingStarted(inspectorCanvas.identifier(), initiator);
 }
@@ -642,8 +696,10 @@ void InspectorCanvasAgent::canvasDestroyedTimerFired()
 
 void InspectorCanvasAgent::clearCanvasData()
 {
-    for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values())
-        inspectorCanvas->context().canvasBase().removeObserver(*this);
+    for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
+        if (auto* context = inspectorCanvas->canvasContext())
+            context->canvasBase().removeObserver(*this);
+    }
 
     m_identifierToInspectorCanvas.clear();
 #if ENABLE(WEBGL)
@@ -660,13 +716,13 @@ InspectorCanvas& InspectorCanvasAgent::bindCanvas(CanvasRenderingContext& contex
     auto inspectorCanvas = InspectorCanvas::create(context);
     m_identifierToInspectorCanvas.set(inspectorCanvas->identifier(), inspectorCanvas.copyRef());
 
-    inspectorCanvas->context().canvasBase().addObserver(*this);
+    context.canvasBase().addObserver(*this);
 
     m_frontendDispatcher->canvasAdded(inspectorCanvas->buildObjectForCanvas(captureBacktrace));
 
 #if ENABLE(WEBGL)
-    if (is<WebGLRenderingContextBase>(inspectorCanvas->context())) {
-        WebGLRenderingContextBase& contextWebGL = downcast<WebGLRenderingContextBase>(inspectorCanvas->context());
+    if (is<WebGLRenderingContextBase>(context)) {
+        auto& contextWebGL = downcast<WebGLRenderingContextBase>(context);
         if (Optional<Vector<String>> extensions = contextWebGL.getSupportedExtensions()) {
             for (const String& extension : *extensions) {
                 if (contextWebGL.extensionIsEnabled(extension))
@@ -679,7 +735,19 @@ InspectorCanvas& InspectorCanvasAgent::bindCanvas(CanvasRenderingContext& contex
     return inspectorCanvas;
 }
 
-String InspectorCanvasAgent::unbindCanvas(InspectorCanvas& inspectorCanvas)
+#if ENABLE(WEBGPU)
+InspectorCanvas& InspectorCanvasAgent::bindCanvas(WebGPUDevice& device, bool captureBacktrace)
+{
+    auto inspectorCanvas = InspectorCanvas::create(device);
+    m_identifierToInspectorCanvas.set(inspectorCanvas->identifier(), inspectorCanvas.copyRef());
+
+    m_frontendDispatcher->canvasAdded(inspectorCanvas->buildObjectForCanvas(captureBacktrace));
+
+    return inspectorCanvas;
+}
+#endif
+
+void InspectorCanvasAgent::unbindCanvas(InspectorCanvas& inspectorCanvas)
 {
 #if ENABLE(WEBGL)
     Vector<InspectorShaderProgram*> programsToRemove;
@@ -692,12 +760,19 @@ String InspectorCanvasAgent::unbindCanvas(InspectorCanvas& inspectorCanvas)
         unbindProgram(*inspectorProgram);
 #endif
 
-    inspectorCanvas.context().canvasBase().removeObserver(*this);
+    if (auto* context = inspectorCanvas.canvasContext())
+        context->canvasBase().removeObserver(*this);
 
     String identifier = inspectorCanvas.identifier();
     m_identifierToInspectorCanvas.remove(identifier);
 
-    return identifier;
+    // This can be called in response to GC. Due to the single-process model used in WebKit1, the
+    // event must be dispatched from a timer to prevent the frontend from making JS allocations
+    // while the GC is still active.
+    m_removedCanvasIdentifiers.append(identifier);
+
+    if (!m_canvasDestroyedTimer.isActive())
+        m_canvasDestroyedTimer.startOneShot(0_s);
 }
 
 RefPtr<InspectorCanvas> InspectorCanvasAgent::assertInspectorCanvas(ErrorString& errorString, const String& canvasId)
@@ -713,12 +788,23 @@ RefPtr<InspectorCanvas> InspectorCanvasAgent::assertInspectorCanvas(ErrorString&
 RefPtr<InspectorCanvas> InspectorCanvasAgent::findInspectorCanvas(CanvasRenderingContext& context)
 {
     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
-        if (&inspectorCanvas->context() == &context)
+        if (inspectorCanvas->canvasContext() == &context)
             return inspectorCanvas;
     }
     return nullptr;
 }
 
+#if ENABLE(WEBGPU)
+RefPtr<InspectorCanvas> InspectorCanvasAgent::findInspectorCanvas(WebGPUDevice& device)
+{
+    for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
+        if (inspectorCanvas->deviceContext() == &device)
+            return inspectorCanvas;
+    }
+    return nullptr;
+}
+#endif
+
 #if ENABLE(WEBGL)
 String InspectorCanvasAgent::unbindProgram(InspectorShaderProgram& inspectorProgram)
 {
index 5bcde7b..baedd46 100644 (file)
@@ -53,6 +53,11 @@ class CanvasRenderingContext;
 class WebGLProgram;
 class WebGLRenderingContextBase;
 #endif
+#if ENABLE(WEBGPU)
+class GPUCanvasContext;
+class WebGPUDevice;
+class WebGPUSwapChain;
+#endif
 
 typedef String ErrorString;
 
@@ -73,8 +78,8 @@ public:
     void disable(ErrorString&);
     void requestNode(ErrorString&, const String& canvasId, int* nodeId);
     void requestContent(ErrorString&, const String& canvasId, String* content);
-    void requestCSSCanvasClientNodes(ErrorString&, const String& canvasId, RefPtr<JSON::ArrayOf<int>>&);
-    void resolveCanvasContext(ErrorString&, const String& canvasId, const String* objectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>&);
+    void requestClientNodes(ErrorString&, const String& canvasId, RefPtr<JSON::ArrayOf<int>>&);
+    void resolveContext(ErrorString&, const String& canvasId, const String* objectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>&);
     void setRecordingAutoCaptureFrameCount(ErrorString&, int count);
     void startRecording(ErrorString&, const String& canvasId, const int* frameCount, const int* memoryLimit);
     void stopRecording(ErrorString&, const String& canvasId);
@@ -92,7 +97,6 @@ public:
     void frameNavigated(Frame&);
     void didChangeCSSCanvasClientNodes(CanvasBase&);
     void didCreateCanvasRenderingContext(CanvasRenderingContext&);
-    void willDestroyCanvasRenderingContext(CanvasRenderingContext&);
     void didChangeCanvasMemory(CanvasRenderingContext&);
     void recordCanvasAction(CanvasRenderingContext&, const String&, std::initializer_list<RecordCanvasActionVariant>&& = { });
     void didFinishRecordingCanvasFrame(CanvasRenderingContext&, bool forceDispatch = false);
@@ -104,6 +108,11 @@ public:
     bool isShaderProgramDisabled(WebGLProgram&);
     bool isShaderProgramHighlighted(WebGLProgram&);
 #endif
+#if ENABLE(WEBGPU)
+    void didCreateWebGPUDevice(WebGPUDevice&);
+    void willDestroyWebGPUDevice(WebGPUDevice&);
+    void willConfigureSwapChain(GPUCanvasContext&, WebGPUSwapChain&);
+#endif
 
 private:
     struct RecordingOptions {
@@ -116,9 +125,16 @@ private:
     void canvasDestroyedTimerFired();
     void clearCanvasData();
     InspectorCanvas& bindCanvas(CanvasRenderingContext&, bool captureBacktrace);
-    String unbindCanvas(InspectorCanvas&);
+#if ENABLE(WEBGPU)
+    InspectorCanvas& bindCanvas(WebGPUDevice&, bool captureBacktrace);
+#endif
+    void unbindCanvas(InspectorCanvas&);
     RefPtr<InspectorCanvas> assertInspectorCanvas(ErrorString&, const String& canvasId);
     RefPtr<InspectorCanvas> findInspectorCanvas(CanvasRenderingContext&);
+#if ENABLE(WEBGPU)
+    RefPtr<InspectorCanvas> findInspectorCanvas(WebGPUDevice&);
+#endif
+
 #if ENABLE(WEBGL)
     String unbindProgram(InspectorShaderProgram&);
     RefPtr<InspectorShaderProgram> assertInspectorProgram(ErrorString&, const String& programId);
index 4ac998f..5a6586c 100644 (file)
@@ -1,3 +1,52 @@
+2019-09-11  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Canvas: instrument WebGPUDevice instead of GPUCanvasContext
+        https://bugs.webkit.org/show_bug.cgi?id=201650
+
+        Reviewed by Joseph Pecoraro.
+
+        Most of the actual "work" done with Web GPU actually uses a `WebGPUDevice`.
+
+        A `GPUCanvasContext` is basically just a display "client" of the device, and isn't even
+        required (e.g. compute pipeline).  We should treat the `GPUCanvasContext` almost like a
+        `-webkit-canvas` client of a `WebGPUDevice`.
+
+        * UserInterface/Protocol/CanvasObserver.js:
+        (WI.CanvasObserver.prototype.clientNodesChanged): Added.
+        (WI.CanvasObserver.prototype.cssCanvasClientNodesChanged):
+        * UserInterface/Controllers/CanvasManager.js:
+        (WI.CanvasManager.prototype.clientNodesChanged): Added.
+        (WI.CanvasManager.prototype.cssCanvasClientNodesChanged): Deleted.
+        * UserInterface/Models/Canvas.js:
+        (WI.Canvas.resetUniqueDisplayNameNumbers):
+        (WI.Canvas.prototype.get displayName):
+        (WI.Canvas.prototype.requestNode):
+        (WI.Canvas.prototype.requestClientNodes): Added.
+        (WI.Canvas.prototype.requestSize):
+        (WI.Canvas.prototype.clientNodesChanged): Added.
+        (WI.Canvas.prototype.requestCSSCanvasClientNodes): Deleted.
+        (WI.Canvas.prototype.cssCanvasClientNodesChanged): Deleted.
+
+        * UserInterface/Protocol/RemoteObject.js:
+        (WI.RemoteObject.resolveCanvasContext):
+
+        * UserInterface/Views/CanvasContentView.js:
+        (WI.CanvasContentView.prototype.attached):
+        (WI.CanvasContentView.prototype._refreshPixelSize):
+        * UserInterface/Views/CanvasDetailsSidebarPanel.js:
+        (WI.CanvasDetailsSidebarPanel.prototype.set canvas):
+        (WI.CanvasDetailsSidebarPanel.prototype.initialLayout):
+        (WI.CanvasDetailsSidebarPanel.prototype.layout):
+        (WI.CanvasDetailsSidebarPanel.prototype._refreshSourceSection):
+        (WI.CanvasDetailsSidebarPanel.prototype._refreshClientsSection): Added.
+        (WI.CanvasDetailsSidebarPanel.prototype._refreshCSSCanvasSection): Deleted.
+        * UserInterface/Views/CanvasOverviewContentView.js:
+        (WI.CanvasOverviewContentView.prototype._contentViewMouseEnter):
+        * UserInterface/Views/CanvasTreeElement.js:
+        (WI.CanvasTreeElement.prototype._handleMouseOver):
+
+        * Localizations/en.lproj/localizedStrings.js:
+
 2019-09-11  Truitt Savell  <tsavell@apple.com>
 
         Unreviewed, rolling out r249753.
index c5e976c..a6aa411 100644 (file)
@@ -194,7 +194,6 @@ localizedStrings["Bytes Sent"] = "Bytes Sent";
 localizedStrings["CPU"] = "CPU";
 localizedStrings["CPU Usage"] = "CPU Usage";
 localizedStrings["CSP Hash"] = "CSP Hash";
-localizedStrings["CSS"] = "CSS";
 localizedStrings["CSS Canvas"] = "CSS Canvas";
 localizedStrings["CSS Changes:"] = "CSS Changes:";
 localizedStrings["CSS canvas \u201C%s\u201D"] = "CSS canvas \u201C%s\u201D";
@@ -246,6 +245,7 @@ localizedStrings["Click to close this tab"] = "Click to close this tab";
 localizedStrings["Click to select a color\nShift-click to switch color formats"] = "Click to select a color\nShift-click to switch color formats";
 localizedStrings["Click to view variable value\nShift-click to replace variable with value"] = "Click to view variable value\nShift-click to replace variable with value";
 localizedStrings["Clickable"] = "Clickable";
+localizedStrings["Clients"] = "Clients";
 localizedStrings["Close"] = "Close";
 localizedStrings["Close %s timeline view"] = "Close %s timeline view";
 localizedStrings["Close Tab"] = "Close Tab";
@@ -345,6 +345,7 @@ localizedStrings["Demo Audit"] = "Demo Audit";
 localizedStrings["Detach into separate window"] = "Detach into separate window";
 localizedStrings["Detached"] = "Detached";
 localizedStrings["Details"] = "Details";
+localizedStrings["Device %d"] = "Device %d";
 localizedStrings["Device Settings"] = "Device Settings";
 localizedStrings["Diagnoses common accessibility problems affecting screen readers and other assistive technology."] = "Diagnoses common accessibility problems affecting screen readers and other assistive technology.";
 localizedStrings["Dimensions"] = "Dimensions";
index 38b9f08..389cb71 100644 (file)
@@ -184,14 +184,14 @@ WI.CanvasManager = class CanvasManager extends WI.Object
         canvas.memoryCost = memoryCost;
     }
 
-    cssCanvasClientNodesChanged(canvasIdentifier)
+    clientNodesChanged(canvasIdentifier)
     {
         let canvas = this._canvasIdentifierMap.get(canvasIdentifier);
         console.assert(canvas);
         if (!canvas)
             return;
 
-        canvas.cssCanvasClientNodesChanged();
+        canvas.clientNodesChanged();
     }
 
     recordingStarted(canvasIdentifier, initiator)
index 28e7858..b929f06 100644 (file)
@@ -41,7 +41,7 @@ WI.Canvas = class Canvas extends WI.Object
         this._memoryCost = memoryCost || NaN;
         this._backtrace = backtrace || [];
 
-        this._cssCanvasClientNodes = null;
+        this._clientNodes = null;
         this._shaderProgramCollection = new WI.ShaderProgramCollection;
         this._recordingCollection = new WI.RecordingCollection;
 
@@ -113,7 +113,8 @@ WI.Canvas = class Canvas extends WI.Object
 
     static resetUniqueDisplayNameNumbers()
     {
-        WI.Canvas._nextUniqueDisplayNameNumber = 1;
+        Canvas._nextContextUniqueDisplayNameNumber = 1;
+        Canvas._nextDeviceUniqueDisplayNameNumber = 1;
     }
 
     // Public
@@ -160,8 +161,14 @@ WI.Canvas = class Canvas extends WI.Object
                 return WI.UIString("Canvas %s").format(idSelector);
         }
 
+        if (this._contextType === Canvas.ContextType.WebGPU) {
+            if (!this._uniqueDisplayNameNumber)
+                this._uniqueDisplayNameNumber = Canvas._nextDeviceUniqueDisplayNameNumber++;
+            return WI.UIString("Device %d").format(this._uniqueDisplayNameNumber);
+        }
+
         if (!this._uniqueDisplayNameNumber)
-            this._uniqueDisplayNameNumber = this.constructor._nextUniqueDisplayNameNumber++;
+            this._uniqueDisplayNameNumber = Canvas._nextContextUniqueDisplayNameNumber++;
         return WI.UIString("Canvas %d").format(this._uniqueDisplayNameNumber);
     }
 
@@ -171,17 +178,22 @@ WI.Canvas = class Canvas extends WI.Object
             this._requestNodePromise = new Promise((resolve, reject) => {
                 WI.domManager.ensureDocument();
 
-                CanvasAgent.requestNode(this._identifier).then((result) => {
-                    this._domNode = WI.domManager.nodeForId(result.nodeId);
+                CanvasAgent.requestNode(this._identifier, (error, nodeId) => {
+                    if (error) {
+                        resolve(null);
+                        return;
+                    }
+
+                    this._domNode = WI.domManager.nodeForId(nodeId);
                     if (!this._domNode) {
-                        reject(`No DOM node for identifier: ${result.nodeId}.`);
+                        resolve(null);
                         return;
                     }
+
                     resolve(this._domNode);
-                }).catch(reject);
+                });
             });
         }
-
         return this._requestNodePromise;
     }
 
@@ -190,30 +202,33 @@ WI.Canvas = class Canvas extends WI.Object
         return CanvasAgent.requestContent(this._identifier).then((result) => result.content).catch((error) => console.error(error));
     }
 
-    requestCSSCanvasClientNodes(callback)
+    requestClientNodes(callback)
     {
-        if (!this._cssCanvasName) {
-            callback([]);
-            return;
-        }
-
-        if (this._cssCanvasClientNodes) {
-            callback(this._cssCanvasClientNodes);
+        if (this._clientNodes) {
+            callback(this._clientNodes);
             return;
         }
 
         WI.domManager.ensureDocument();
 
-        CanvasAgent.requestCSSCanvasClientNodes(this._identifier, (error, clientNodeIds) => {
+        let wrappedCallback = (error, clientNodeIds) => {
             if (error) {
                 callback([]);
                 return;
             }
 
             clientNodeIds = Array.isArray(clientNodeIds) ? clientNodeIds : [];
-            this._cssCanvasClientNodes = clientNodeIds.map((clientNodeId) => WI.domManager.nodeForId(clientNodeId));
-            callback(this._cssCanvasClientNodes);
-        });
+            this._clientNodes = clientNodeIds.map((clientNodeId) => WI.domManager.nodeForId(clientNodeId));
+            callback(this._clientNodes);
+        };
+
+        // COMPATIBILITY (iOS 13): Canvas.requestCSSCanvasClientNodes was renamed to Canvas.requestClientNodes.
+        if (!CanvasAgent.requestClientNodes) {
+            CanvasAgent.requestCSSCanvasClientNodes(this._identifier, wrappedCallback);
+            return;
+        }
+
+        CanvasAgent.requestClientNodes(this._identifier, wrappedCallback);
     }
 
     requestSize()
@@ -245,6 +260,9 @@ WI.Canvas = class Canvas extends WI.Object
         }
 
         return this.requestNode().then((domNode) => {
+            if (!domNode)
+                return null;
+
             let size = calculateSize(domNode);
             if (!isNaN(size.width) && !isNaN(size.height))
                 return size;
@@ -327,16 +345,13 @@ WI.Canvas = class Canvas extends WI.Object
         this.dispatchEventToListeners(WI.Canvas.Event.ExtensionEnabled, {extension});
     }
 
-    cssCanvasClientNodesChanged()
+    clientNodesChanged()
     {
         // Called from WI.CanvasManager.
 
-        if (!this._cssCanvasName)
-            return;
-
-        this._cssCanvasClientNodes = null;
+        this._clientNodes = null;
 
-        this.dispatchEventToListeners(WI.Canvas.Event.CSSCanvasClientNodesChanged);
+        this.dispatchEventToListeners(Canvas.Event.ClientNodesChanged);
     }
 
     recordingStarted(initiator)
@@ -402,7 +417,8 @@ WI.Canvas = class Canvas extends WI.Object
     }
 };
 
-WI.Canvas._nextUniqueDisplayNameNumber = 1;
+WI.Canvas._nextContextUniqueDisplayNameNumber = 1;
+WI.Canvas._nextDeviceUniqueDisplayNameNumber = 1;
 
 WI.Canvas.FrameURLCookieKey = "canvas-frame-url";
 WI.Canvas.CSSCanvasNameCookieKey = "canvas-css-canvas-name";
@@ -426,7 +442,7 @@ WI.Canvas.RecordingState = {
 WI.Canvas.Event = {
     MemoryChanged: "canvas-memory-changed",
     ExtensionEnabled: "canvas-extension-enabled",
-    CSSCanvasClientNodesChanged: "canvas-css-canvas-client-nodes-changed",
+    ClientNodesChanged: "canvas-client-nodes-changed",
     RecordingStarted: "canvas-recording-started",
     RecordingProgress: "canvas-recording-progress",
     RecordingStopped: "canvas-recording-stopped",
index 4874337..3e5dd6c 100644 (file)
@@ -42,9 +42,9 @@ WI.CanvasObserver = class CanvasObserver
         WI.canvasManager.canvasMemoryChanged(canvasId, memoryCost);
     }
 
-    cssCanvasClientNodesChanged(canvasId)
+    clientNodesChanged(canvasId)
     {
-        WI.canvasManager.cssCanvasClientNodesChanged(canvasId);
+        WI.canvasManager.clientNodesChanged(canvasId);
     }
 
     recordingStarted(canvasId, initiator)
@@ -76,4 +76,10 @@ WI.CanvasObserver = class CanvasObserver
     {
         WI.canvasManager.programDeleted(programId);
     }
+
+    // COMPATIBILITY (iOS 13): Canvas.events.cssCanvasClientNodesChanged was renamed to Canvas.events.clientNodesChanged.
+    cssCanvasClientNodesChanged(canvasId)
+    {
+        WI.canvasManager.clientNodesChanged(canvasId);
+    }
 };
index 0b44a40..e51d583 100644 (file)
@@ -163,12 +163,20 @@ WI.RemoteObject = class RemoteObject
     {
         console.assert(typeof callback === "function");
 
-        CanvasAgent.resolveCanvasContext(canvas.identifier, objectGroup, (error, object) => {
+        function wrapCallback(error, object) {
             if (error || !object)
                 callback(null);
             else
                 callback(WI.RemoteObject.fromPayload(object, WI.mainTarget));
-        });
+        }
+
+        // COMPATIBILITY (iOS 13): Canvas.resolveCanvasContext was renamed to Canvas.resolveContext.
+        if (!CanvasAgent.resolveContext) {
+            CanvasAgent.resolveCanvasContext(canvas.identifier, objectGroup, wrapCallback);
+            return;
+        }
+
+        CanvasAgent.resolveContext(canvas.identifier, objectGroup, wrapCallback);
     }
 
     // Public
index 92c25cc..4b3b050 100644 (file)
@@ -210,6 +210,9 @@ WI.CanvasContentView = class CanvasContentView extends WI.ContentView
         this.representedObject.shaderProgramCollection.addEventListener(WI.Collection.Event.ItemRemoved, this.needsLayout, this);
 
         this.representedObject.requestNode().then((node) => {
+            if (!node)
+                return;
+
             console.assert(!this._canvasNode || this._canvasNode === node);
             if (this._canvasNode === node)
                 return;
@@ -281,12 +284,8 @@ WI.CanvasContentView = class CanvasContentView extends WI.ContentView
             this.refreshPreview();
         };
 
-        this.representedObject.requestSize()
-        .then((size) => {
+        this.representedObject.requestSize().then((size) => {
             updatePixelSize(size);
-        })
-        .catch((error) => {
-            updatePixelSize(null);
         });
     }
 
index d1988ff..d993b05 100644 (file)
@@ -78,7 +78,7 @@ WI.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends WI.Detail
         if (this._canvas) {
             this._canvas.removeEventListener(WI.Canvas.Event.MemoryChanged, this._canvasMemoryChanged, this);
             this._canvas.removeEventListener(WI.Canvas.Event.ExtensionEnabled, this._refreshExtensionsSection, this);
-            this._canvas.removeEventListener(WI.Canvas.Event.CSSCanvasClientNodesChanged, this._refreshCSSCanvasSection, this);
+            this._canvas.removeEventListener(WI.Canvas.Event.ClientNodesChanged, this._refreshClientsSection, this);
         }
 
         this._canvas = canvas || null;
@@ -86,7 +86,7 @@ WI.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends WI.Detail
         if (this._canvas) {
             this._canvas.addEventListener(WI.Canvas.Event.MemoryChanged, this._canvasMemoryChanged, this);
             this._canvas.addEventListener(WI.Canvas.Event.ExtensionEnabled, this._refreshExtensionsSection, this);
-            this._canvas.addEventListener(WI.Canvas.Event.CSSCanvasClientNodesChanged, this._refreshCSSCanvasSection, this);
+            this._canvas.addEventListener(WI.Canvas.Event.ClientNodesChanged, this._refreshClientsSection, this);
         }
 
         this.needsLayout();
@@ -111,10 +111,10 @@ WI.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends WI.Detail
         this._cssCanvasRow = new WI.DetailsSectionSimpleRow(WI.UIString("CSS Canvas"));
         this._widthRow = new WI.DetailsSectionSimpleRow(WI.UIString("Width"));
         this._heightRow = new WI.DetailsSectionSimpleRow(WI.UIString("Height"));
-        this._datachedRow = new WI.DetailsSectionSimpleRow(WI.UIString("Detached"));
+        this._detachedRow = new WI.DetailsSectionSimpleRow(WI.UIString("Detached"));
 
         let sourceSection = new WI.DetailsSection("canvas-source", WI.UIString("Source"));
-        sourceSection.groups = [new WI.DetailsSectionGroup([this._nodeRow, this._cssCanvasRow, this._widthRow, this._heightRow, this._datachedRow])];
+        sourceSection.groups = [new WI.DetailsSectionGroup([this._nodeRow, this._cssCanvasRow, this._widthRow, this._heightRow, this._detachedRow])];
         this._sections.push(sourceSection);
 
         this._attributesDataGridRow = new WI.DetailsSectionDataGridRow(null, WI.UIString("No Attributes"));
@@ -128,12 +128,12 @@ WI.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends WI.Detail
         this._extensionsSection.element.hidden = true;
         this._sections.push(this._extensionsSection);
 
-        this._cssCanvasClientsRow = new WI.DetailsSectionSimpleRow(WI.UIString("Nodes"));
+        this._clientNodesRow = new WI.DetailsSectionSimpleRow(WI.UIString("Nodes"));
 
-        this._cssCanvasSection = new WI.DetailsSection("canvas-css", WI.UIString("CSS"));
-        this._cssCanvasSection.groups = [new WI.DetailsSectionGroup([this._cssCanvasClientsRow])];
-        this._cssCanvasSection.element.hidden = true;
-        this._sections.push(this._cssCanvasSection);
+        this._clientsSection = new WI.DetailsSection("canvas-clients", WI.UIString("Clients"));
+        this._clientsSection.groups = [new WI.DetailsSectionGroup([this._clientNodesRow])];
+        this._clientsSection.element.hidden = true;
+        this._sections.push(this._clientsSection);
 
         const selectable = false;
         let backtraceTreeOutline = new WI.TreeOutline(selectable);
@@ -168,7 +168,7 @@ WI.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends WI.Detail
         this._refreshSourceSection();
         this._refreshAttributesSection();
         this._refreshExtensionsSection();
-        this._refreshCSSCanvasSection();
+        this._refreshClientsSection();
         this._refreshBacktraceSection();
     }
 
@@ -194,13 +194,20 @@ WI.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends WI.Detail
         if (!this.didInitialLayout)
             return;
 
-        this._nodeRow.value = this._canvas.cssCanvasName ? null : emDash;
+        let hideNode = this._canvas.cssCanvasName || this._canvas.contextType === WI.Canvas.ContextType.WebGPU;
+
+        this._nodeRow.value = hideNode ? null : emDash;
         this._cssCanvasRow.value = this._canvas.cssCanvasName || null;
         this._widthRow.value = emDash;
         this._heightRow.value = emDash;
-        this._datachedRow.value = null;
+        this._detachedRow.value = null;
 
         this._canvas.requestNode().then((node) => {
+            if (!node) {
+                this._nodeRow.value = null;
+                return;
+            }
+
             if (node !== this._node) {
                 if (this._node) {
                     this._node.removeEventListener(WI.DOMNode.Event.AttributeModified, this._refreshSourceSection, this);
@@ -215,9 +222,13 @@ WI.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends WI.Detail
                 this._node.addEventListener(WI.DOMNode.Event.AttributeRemoved, this._refreshSourceSection, this);
             }
 
-            if (!this._canvas.cssCanvasName)
+            if (!hideNode) {
                 this._nodeRow.value = WI.linkifyNodeReference(this._node);
 
+                if (!this._node.parentNode)
+                    this._detachedRow.value = WI.UIString("Yes");
+            }
+
             let setRowValueIfValidAttributeValue = (row, attribute) => {
                 let value = Number(this._node.getAttribute(attribute));
                 if (!Number.isInteger(value) || value < 0)
@@ -248,9 +259,6 @@ WI.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends WI.Detail
                     remoteObject.release();
                 });
             }
-
-            if (!this._canvas.cssCanvasName && !this._node.parentNode)
-                this._datachedRow.value = WI.UIString("Yes");
         });
     }
 
@@ -295,28 +303,28 @@ WI.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends WI.Detail
         this._extensionsSection.groups = [{element}];
     }
 
-    _refreshCSSCanvasSection()
+    _refreshClientsSection()
     {
         if (!this.didInitialLayout)
             return;
 
-        if (!this._canvas.cssCanvasName) {
-            this._cssCanvasSection.element.hidden = true;
+        if (!this._canvas.cssCanvasName && this._canvas.contextType !== WI.Canvas.ContextType.WebGPU) {
+            this._clientsSection.element.hidden = true;
             return;
         }
 
-        this._cssCanvasClientsRow.value = emDash;
+        this._clientNodesRow.value = emDash;
 
-        this._cssCanvasSection.element.hidden = false;
+        this._clientsSection.element.hidden = false;
 
-        this._canvas.requestCSSCanvasClientNodes((cssCanvasClientNodes) => {
-            if (!cssCanvasClientNodes.length)
+        this._canvas.requestClientNodes((clientNodes) => {
+            if (!clientNodes.length)
                 return;
 
             let fragment = document.createDocumentFragment();
-            for (let clientNode of cssCanvasClientNodes)
+            for (let clientNode of clientNodes)
                 fragment.appendChild(WI.linkifyNodeReference(clientNode));
-            this._cssCanvasClientsRow.value = fragment;
+            this._clientNodesRow.value = fragment;
         });
     }
 
index 2ef64f0..cb33a7a 100644 (file)
@@ -191,9 +191,9 @@ WI.CanvasOverviewContentView = class CanvasOverviewContentView extends WI.Collec
             return;
 
         let canvas = contentView.representedObject;
-        if (canvas.cssCanvasName) {
-            canvas.requestCSSCanvasClientNodes((cssCanvasClientNodes) => {
-                WI.domManager.highlightDOMNodeList(cssCanvasClientNodes.map((node) => node.id));
+        if (canvas.cssCanvasName || canvas.contextType === WI.Canvas.ContextType.WebGPU) {
+            canvas.requestClientNodes((clientNodes) => {
+                WI.domManager.highlightDOMNodeList(clientNodes.map((node) => node.id));
             });
             return;
         }
index 144d42a..6b37352 100644 (file)
@@ -115,16 +115,16 @@ WI.CanvasTreeElement = class CanvasTreeElement extends WI.FolderizedTreeElement
 
     _handleMouseOver(event)
     {
-        if (this.representedObject.cssCanvasName) {
-            this.representedObject.requestCSSCanvasClientNodes((cssCanvasClientNodes) => {
-                WI.domManager.highlightDOMNodeList(cssCanvasClientNodes.map((node) => node.id), "all");
+        if (this.representedObject.cssCanvasName || this.representedObject.contextType === WI.Canvas.ContextType.WebGPU) {
+            this.representedObject.requestClientNodes((clientNodes) => {
+                WI.domManager.highlightDOMNodeList(clientNodes.map((node) => node.id));
             });
         } else {
             this.representedObject.requestNode((node) => {
                 if (!node || !node.ownerDocument)
                     return;
 
-                WI.domManager.highlightDOMNode(node.id, "all");
+                WI.domManager.highlightDOMNode(node.id);
             });
         }
     }