Title: [218544] trunk
Revision
218544
Author
drou...@apple.com
Date
2017-06-19 23:48:10 -0700 (Mon, 19 Jun 2017)

Log Message

Web Inspector: create canvas content view and details sidebar panel
https://bugs.webkit.org/show_bug.cgi?id=138941
<rdar://problem/19051672>

Reviewed by Joseph Pecoraro.

Source/_javascript_Core:

* inspector/protocol/Canvas.json:
 - Add an optional `nodeId` attribute to the `Canvas` type.
 - Add `requestNode` command for getting the node id of the backing canvas element.
 - Add `requestContent` command for getting the current image content of the canvas.

Source/WebCore:

Tests: inspector/canvas/requestContent.html
       inspector/canvas/requestNode.html

* inspector/InspectorCanvasAgent.h:
* inspector/InspectorCanvasAgent.cpp:
(WebCore::InspectorCanvasAgent::requestNode):
Gets the node id of the backing canvas element.

(WebCore::InspectorCanvasAgent::requestContent):
Gets the current image content of the canvas.

(WebCore::InspectorCanvasAgent::frameNavigated):
(WebCore::InspectorCanvasAgent::didCreateCanvasRenderingContext):
Minor fixes from r218376 <https://webkit.org/b/172623>.

(WebCore::InspectorCanvasAgent::buildObjectForCanvas):
Optionally send the `nodeId` of the backing canvas element if it is available.

* inspector/InspectorInstrumentation.cpp:
(WebCore::InspectorInstrumentation::didCommitLoadImpl):

Source/WebInspectorUI:

* UserInterface/Models/Canvas.js:
(WebInspector.Canvas):
(WebInspector.Canvas.fromPayload):
(WebInspector.Canvas.prototype.get displayName):
(WebInspector.Canvas.requestNode): Added.
(WebInspector.Canvas.prototype.requestContent): Added.
(WebInspector.Canvas.prototype.saveIdentityToCookie):

* UserInterface/Controllers/CanvasManager.js:
(WebInspector.CanvasManager.prototype.canvasAdded):
(WebInspector.CanvasManager.prototype.canvasRemoved):
* UserInterface/Models/Collection.js:
* UserInterface/Models/Frame.js:
(WebInspector.Frame):
(WebInspector.Frame.prototype.get canvasCollection):
(WebInspector.Frame.prototype.commitProvisionalLoad):
Create a Collection for Canvas in each Frame, and modify it when canvas events are fired.

* UserInterface/Views/CanvasContentView.css: Added.
(.content-view.canvas > .preview):
(.content-view.canvas > .preview > img):
* UserInterface/Views/CanvasContentView.js: Added.
(WebInspector.CanvasContentView):
(WebInspector.CanvasContentView.prototype.get navigationItems):
(WebInspector.CanvasContentView.prototype.shown):
(WebInspector.CanvasContentView.prototype.hidden):
(WebInspector.CanvasContentView.prototype._showPreview):
(WebInspector.CanvasContentView.prototype._updateImageGrid):
(WebInspector.CanvasContentView.prototype._showGridButtonClicked):
* UserInterface/Views/CanvasDetailsSidebarPanel.js: Added.
(WebInspector.CanvasDetailsSidebarPanel):
(WebInspector.CanvasDetailsSidebarPanel.prototype.inspect):
(WebInspector.CanvasDetailsSidebarPanel.prototype.get canvas):
(WebInspector.CanvasDetailsSidebarPanel.prototype.set canvas):
(WebInspector.CanvasDetailsSidebarPanel.prototype.initialLayout):
(WebInspector.CanvasDetailsSidebarPanel.prototype.layout):
(WebInspector.CanvasDetailsSidebarPanel.prototype._refreshIdentitySection):
(WebInspector.CanvasDetailsSidebarPanel.prototype._refreshSourceSection.this._canvas.requestNode.):
(WebInspector.CanvasDetailsSidebarPanel.prototype._refreshSourceSection):
* UserInterface/Views/CanvasTreeElement.js: Added.
(WebInspector.CanvasTreeElement):

* UserInterface/Views/FrameTreeElement.js:
(WebInspector.FrameTreeElement.prototype.onattach):
(WebInspector.FrameTreeElement.prototype.ondetach):
(WebInspector.FrameTreeElement.prototype.onpopulate):
(WebInspector.FrameTreeElement.prototype._canvasWasAdded):
(WebInspector.FrameTreeElement.prototype._canvasWasRemoved):
(WebInspector.FrameTreeElement):
* UserInterface/Base/Main.js:
* UserInterface/Views/ContentView.js:
(WebInspector.ContentView.createFromRepresentedObject):
(WebInspector.ContentView.isViewable):
* UserInterface/Views/ResourceSidebarPanel.js:
(WebInspector.ResourceSidebarPanel):
(WebInspector.ResourceSidebarPanel.prototype.matchTreeElementAgainstCustomFilters.match):
(WebInspector.ResourceSidebarPanel.prototype._treeSelectionDidChange):
* UserInterface/Views/ResourcesTabContentView.js:
(WebInspector.ResourcesTabContentView):
(WebInspector.ResourcesTabContentView.prototype.canShowRepresentedObject):
Show Canvas objects and tie them to the correct ContentView and TreeElement subclasses.

* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Main.html:
* UserInterface/Images/Canvas.svg: Added.
* UserInterface/Views/ResourceIcons.css:
(.canvas .icon):
Added new files/rules/strings related to Canvas UI.

* UserInterface/Views/SettingsTabContentView.css:
(.content-view.settings .navigation-bar):

* UserInterface/Base/Setting.js:
* UserInterface/Views/SettingsTabContentView.js:
(WebInspector.SettingsTabContentView.prototype.initialLayout):
(WebInspector.SettingsTabContentView.prototype._createGeneralSettingsView):
(WebInspector.SettingsTabContentView.prototype._createDebugSettingsView):
Add an experimental settings toggle in the Debug view for showing canvas contexts.

* UserInterface/Views/FolderizedTreeElement.js:
(WebInspector.FolderizedTreeElement.prototype._compareTreeElementsByMainTitle):
Drive-by fix: ensure that sorting also includes numbers, so that 1 < 2 < 10.

* UserInterface/Views/ImageResourceContentView.js:
(WebInspector.ImageResourceContentView.prototype.shown):
(WebInspector.ImageResourceContentView.prototype.hidden):
(WebInspector.ImageResourceContentView.prototype._updateImageGrid):
(WebInspector.ImageResourceContentView.prototype._showGridButtonClicked):
(WebInspector.ImageResourceContentView):
Drive-by fix: change the activated state of the Show Grid navigation item if it changes
in another view.

LayoutTests:

* inspector/canvas/requestContent-expected.txt: Added.
* inspector/canvas/requestContent.html: Added.
* inspector/canvas/requestNode-expected.txt: Added.
* inspector/canvas/requestNode.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (218543 => 218544)


--- trunk/LayoutTests/ChangeLog	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/LayoutTests/ChangeLog	2017-06-20 06:48:10 UTC (rev 218544)
@@ -1,3 +1,16 @@
+2017-06-19  Devin Rousso  <drou...@apple.com>
+
+        Web Inspector: create canvas content view and details sidebar panel
+        https://bugs.webkit.org/show_bug.cgi?id=138941
+        <rdar://problem/19051672>
+
+        Reviewed by Joseph Pecoraro.
+
+        * inspector/canvas/requestContent-expected.txt: Added.
+        * inspector/canvas/requestContent.html: Added.
+        * inspector/canvas/requestNode-expected.txt: Added.
+        * inspector/canvas/requestNode.html: Added.
+
 2017-06-19  Frederic Wang  <fw...@igalia.com>
 
         [iOS] Always include frames in the scrolling tree when async frame scrolling is enabled

Added: trunk/LayoutTests/inspector/canvas/requestContent-expected.txt (0 => 218544)


--- trunk/LayoutTests/inspector/canvas/requestContent-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/inspector/canvas/requestContent-expected.txt	2017-06-20 06:48:10 UTC (rev 218544)
@@ -0,0 +1,19 @@
+Test that CanvasAgent.requestContent can properly resolve the owner canvas node.
+
+
+== Running test suite: Canvas.requestContent
+-- Running test case: Canvas.requestContent.validCanvasId
+Canvas "CSS canvas “css”" has content:
+data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAACJJREFUGBljYKA2YEQy8D8SG5kJVsOELIKPPRQU4nM/eXIARtsBDD0MXU8AAAAASUVORK5CYII=
+
+Canvas "Canvas 1" has content:
+data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAACWCAYAAABkW7XSAAAAAXNSR0IArs4c6QAABE9JREFUeAHt1LENACEQA0Ge/nvmIwoguGQ1JBAheSx5LYcAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQLDAt/w//f7cx9uAgSyAuN7srN0ghEgkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5A
 TMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI0CAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBF4FfmEuAchmzh6GAAAAAElFTkSuQmCC
+
+Canvas "Canvas 2" has content:
+data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAACWCAYAAABkW7XSAAAAAXNSR0IArs4c6QAABE9JREFUeAHt1LENACEQA0Ge/nvmIwoguGQ1JBAheSx5LYcAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQLDAt/w//f7cx9uAgSyAuN7srN0ghEgkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5A
 TMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI5ATMFi5SgUi0BUwWN1uJSOQEzBYuUoFItAVMFjdbiUjkBMwWLlKBSLQFTBY3W4lI0CAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBF4FfmEuAchmzh6GAAAAAElFTkSuQmCC
+
+
+-- Running test case: Canvas.requestContent.invalidCanvasId
+PASS: Should produce an error.
+Error: Invalid canvas identifier
+

Added: trunk/LayoutTests/inspector/canvas/requestContent.html (0 => 218544)


--- trunk/LayoutTests/inspector/canvas/requestContent.html	                        (rev 0)
+++ trunk/LayoutTests/inspector/canvas/requestContent.html	2017-06-20 06:48:10 UTC (rev 218544)
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script>
+function load() {
+    window.contextOnscreen = document.getElementById("onscreen").getContext("2d");
+    window.contextOnscreen.fillRect(25, 25, 250, 100);
+
+    window.contextOffscreen = document.createElement("canvas").getContext("2d");
+    window.contextOffscreen.fillRect(25, 25, 250, 100);
+
+    window.contextCSS = document.getCSSCanvasContext("2d", "css", 10, 10);
+    window.contextCSS.fillRect(2, 2, 6, 6);
+
+    runTest();
+}
+
+function test() {
+    let suite = InspectorTest.createAsyncSuite("Canvas.requestContent");
+
+    suite.addTestCase({
+        name: "Canvas.requestContent.validCanvasId",
+        description: "Get the base64 encoded data for each canvas on the page.",
+        test(resolve, reject) {
+            let canvases = WebInspector.canvasManager.canvases;
+            let expectedLength = canvases.length;
+            InspectorTest.assert(expectedLength === 3, "The page has 3 canvases.");
+
+            let contentsMap = new Map;
+
+            function finish() {
+                let results = {};
+                for (let [canvas, content] of contentsMap)
+                    results[canvas.displayName] = content;
+
+                let keys = Object.keys(results);
+                InspectorTest.assert(keys.length === contentsMap.size, "No display name collisions");
+
+                // Ensure that the test runs properly even if the canvas
+                // events are sent in a different order than the above.
+                keys.sort();
+                for (let displayName of keys) {
+                    InspectorTest.log(`Canvas "${displayName}" has content:`);
+                    InspectorTest.log(results[displayName]);
+                    InspectorTest.log("");
+                }
+
+                resolve();
+            }
+
+            canvases.forEach((canvas) => {
+                CanvasAgent.requestContent(canvas.identifier, (error, content) => {
+                    if (error) {
+                        reject(error);
+                        return;
+                    }
+
+                    contentsMap.set(canvas, content);
+                    if (contentsMap.size === expectedLength)
+                        finish();
+                });
+            });
+        }
+    });
+
+    // ------
+
+    suite.addTestCase({
+        name: "Canvas.requestContent.invalidCanvasId",
+        description: "Invalid canvas identifiers should cause an error.",
+        test(resolve, reject) {
+            const canvasId = "DOES_NOT_EXIST";
+            CanvasAgent.requestContent(canvasId, (error) => {
+                InspectorTest.expectThat(error, "Should produce an error.");
+                InspectorTest.log("Error: " + error);
+                resolve();
+            });
+        }
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+<style>
+    .css { background-image: -webkit-canvas(css); }
+</style>
+</head>
+<body _onload_="load()">
+<p>Test that CanvasAgent.requestContent can properly resolve the owner canvas node.</p>
+<canvas id="onscreen"></canvas>
+</body>
+</html>

Added: trunk/LayoutTests/inspector/canvas/requestNode-expected.txt (0 => 218544)


--- trunk/LayoutTests/inspector/canvas/requestNode-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/inspector/canvas/requestNode-expected.txt	2017-06-20 06:48:10 UTC (rev 218544)
@@ -0,0 +1,21 @@
+Test that CanvasAgent.requestNode can properly resolve the owner canvas node.
+
+
+== Running test suite: Canvas.requestNode
+-- Running test case: Canvas.requestNode.missingDocument
+PASS: The page should have three canvases.
+PASS: Should produce an error.
+Error: Document has not been requested
+
+-- Running test case: Canvas.requestNode.validCanvasId
+PASS: Canvas "CSS canvas “css”" has node with valid id.
+PASS: Canvas "CSS canvas “css”" has node with type "CANVAS".
+PASS: Canvas "Canvas 1" has node with valid id.
+PASS: Canvas "Canvas 1" has node with type "CANVAS".
+PASS: Canvas "Canvas 2" has node with valid id.
+PASS: Canvas "Canvas 2" has node with type "CANVAS".
+
+-- Running test case: Canvas.requestNode.invalidCanvasId
+PASS: Should produce an error.
+Error: Invalid canvas identifier
+

Added: trunk/LayoutTests/inspector/canvas/requestNode.html (0 => 218544)


--- trunk/LayoutTests/inspector/canvas/requestNode.html	                        (rev 0)
+++ trunk/LayoutTests/inspector/canvas/requestNode.html	2017-06-20 06:48:10 UTC (rev 218544)
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script>
+function load() {
+    window._onscreenCanvas_ = document.getElementById("onscreen").getContext("2d");
+    window.offscreenCanvas = document.createElement("canvas").getContext("2d");
+    window.cssCanvas = document.getCSSCanvasContext("2d", "css", 10, 10);
+
+    runTest();
+}
+
+function test() {
+    let suite = InspectorTest.createAsyncSuite("Canvas.requestNode");
+
+    suite.addTestCase({
+        name: "Canvas.requestNode.missingDocument",
+        description: "Getting the canvas node requires that WebInspector knows about the document.",
+        test(resolve, reject) {
+            let canvases = WebInspector.canvasManager.canvases;
+            InspectorTest.expectEqual(canvases.length, 3, "The page should have three canvases.");
+            if (!canvases.length) {
+                reject("Missing canvas.");
+                return;
+            }
+
+            CanvasAgent.requestNode(canvases[0].identifier, (error) => {
+                InspectorTest.expectThat(error, "Should produce an error.");
+                InspectorTest.log("Error: " + error);
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "Canvas.requestNode.validCanvasId",
+        description: "Get the node id for each canvas on the page.",
+        test(resolve, reject) {
+            let canvases = WebInspector.canvasManager.canvases;
+            let expectedLength = canvases.length;
+            InspectorTest.assert(expectedLength === 3, "The page has 3 canvases.");
+
+            let nodesMap = new Map;
+
+            function finish() {
+                let results = {};
+                for (let [canvas, node] of nodesMap)
+                    results[canvas.displayName] = node;
+
+                let keys = Object.keys(results);
+                InspectorTest.assert(keys.length === nodesMap.size, "No display name collisions");
+
+                // Ensure that the test runs properly even if the canvas
+                // events are sent in a different order than the above.
+                keys.sort();
+                for (let displayName of keys) {
+                    InspectorTest.expectThat(results[displayName], `Canvas "${displayName}" has node with valid id.`);
+                    InspectorTest.expectEqual(results[displayName].nodeName(), "CANVAS", `Canvas "${displayName}" has node with type "CANVAS".`);
+                }
+
+                resolve();
+            }
+
+            canvases.forEach((canvas) => {
+                WebInspector.domTreeManager.requestDocument((documentNode) => {
+                    CanvasAgent.requestNode(canvas.identifier, (error, nodeId) => {
+                        if (error) {
+                            reject(error);
+                            return;
+                        }
+
+                        nodesMap.set(canvas, WebInspector.domTreeManager.nodeForId(nodeId));
+                        if (nodesMap.size === expectedLength)
+                            finish();
+                    });
+                });
+            });
+        }
+    });
+
+    // ------
+
+    suite.addTestCase({
+        name: "Canvas.requestNode.invalidCanvasId",
+        description: "Invalid canvas identifiers should cause an error.",
+        test(resolve, reject) {
+            const canvasId = "DOES_NOT_EXIST";
+            CanvasAgent.requestNode(canvasId, (error) => {
+                InspectorTest.expectThat(error, "Should produce an error.");
+                InspectorTest.log("Error: " + error);
+                resolve();
+            });
+        }
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+<style>
+    .css { background-image: -webkit-canvas(css); }
+</style>
+</head>
+<body _onload_="load()">
+<p>Test that CanvasAgent.requestNode can properly resolve the owner canvas node.</p>
+<canvas id="onscreen"></canvas>
+</body>
+</html>

Modified: trunk/Source/_javascript_Core/ChangeLog (218543 => 218544)


--- trunk/Source/_javascript_Core/ChangeLog	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-06-20 06:48:10 UTC (rev 218544)
@@ -1,3 +1,16 @@
+2017-06-19  Devin Rousso  <drou...@apple.com>
+
+        Web Inspector: create canvas content view and details sidebar panel
+        https://bugs.webkit.org/show_bug.cgi?id=138941
+        <rdar://problem/19051672>
+
+        Reviewed by Joseph Pecoraro.
+
+        * inspector/protocol/Canvas.json:
+         - Add an optional `nodeId` attribute to the `Canvas` type.
+         - Add `requestNode` command for getting the node id of the backing canvas element.
+         - Add `requestContent` command for getting the current image content of the canvas.
+
 2017-06-19  Yusuke Suzuki  <utatane....@gmail.com>
 
         Unreviewed, build fix for ARM

Modified: trunk/Source/_javascript_Core/inspector/protocol/Canvas.json (218543 => 218544)


--- trunk/Source/_javascript_Core/inspector/protocol/Canvas.json	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/_javascript_Core/inspector/protocol/Canvas.json	2017-06-20 06:48:10 UTC (rev 218544)
@@ -22,6 +22,7 @@
                 { "name": "canvasId", "$ref": "CanvasId", "description": "Canvas identifier." },
                 { "name": "contextType", "$ref": "ContextType", "description": "The type of rendering context backing the canvas." },
                 { "name": "frameId", "$ref": "Network.FrameId", "description": "Parent frame identifier." },
+                { "name": "nodeId", "$ref": "DOM.NodeId", "optional": true, "description": "The corresponding DOM node id." },
                 { "name": "cssCanvasName", "type": "string", "optional": true, "description": "The CSS canvas identifier, for canvases created with <code>document.getCSSCanvasContext</code>." }
             ]
         }
@@ -34,6 +35,26 @@
         {
             "name": "disable",
             "description": "Disables Canvas domain events."
+        },
+        {
+            "name": "requestNode",
+            "description": "Gets the NodeId for the canvas node with the given CanvasId.",
+            "parameters": [
+                { "name": "canvasId", "$ref": "CanvasId", "description": "Canvas identifier." }
+            ],
+            "returns": [
+                { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Node identifier for given canvas." }
+            ]
+        },
+        {
+            "name": "requestContent",
+            "description": "Gets the data for the canvas node with the given CanvasId.",
+            "parameters": [
+                { "name": "canvasId", "$ref": "CanvasId", "description": "Canvas identifier." }
+            ],
+            "returns": [
+                { "name": "content", "type": "string", "description": "Base64-encoded data of the canvas' contents." }
+            ]
         }
     ],
     "events": [

Modified: trunk/Source/WebCore/ChangeLog (218543 => 218544)


--- trunk/Source/WebCore/ChangeLog	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebCore/ChangeLog	2017-06-20 06:48:10 UTC (rev 218544)
@@ -1,3 +1,32 @@
+2017-06-19  Devin Rousso  <drou...@apple.com>
+
+        Web Inspector: create canvas content view and details sidebar panel
+        https://bugs.webkit.org/show_bug.cgi?id=138941
+        <rdar://problem/19051672>
+
+        Reviewed by Joseph Pecoraro.
+
+        Tests: inspector/canvas/requestContent.html
+               inspector/canvas/requestNode.html
+
+        * inspector/InspectorCanvasAgent.h:
+        * inspector/InspectorCanvasAgent.cpp:
+        (WebCore::InspectorCanvasAgent::requestNode):
+        Gets the node id of the backing canvas element.
+
+        (WebCore::InspectorCanvasAgent::requestContent):
+        Gets the current image content of the canvas.
+
+        (WebCore::InspectorCanvasAgent::frameNavigated):
+        (WebCore::InspectorCanvasAgent::didCreateCanvasRenderingContext):
+        Minor fixes from r218376 <https://webkit.org/b/172623>.
+
+        (WebCore::InspectorCanvasAgent::buildObjectForCanvas):
+        Optionally send the `nodeId` of the backing canvas element if it is available.
+
+        * inspector/InspectorInstrumentation.cpp:
+        (WebCore::InspectorInstrumentation::didCommitLoadImpl):
+
 2017-06-19  Frederic Wang  <fw...@igalia.com>
 
         [iOS] Always include frames in the scrolling tree when async frame scrolling is enabled

Modified: trunk/Source/WebCore/inspector/InspectorCanvasAgent.cpp (218543 => 218544)


--- trunk/Source/WebCore/inspector/InspectorCanvasAgent.cpp	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebCore/inspector/InspectorCanvasAgent.cpp	2017-06-20 06:48:10 UTC (rev 218544)
@@ -27,8 +27,8 @@
 #include "InspectorCanvasAgent.h"
 
 #include "CanvasRenderingContext.h"
+#include "CanvasRenderingContext2D.h"
 #include "Document.h"
-#include "DocumentLoader.h"
 #include "Frame.h"
 #include "InspectorDOMAgent.h"
 #include "InspectorPageAgent.h"
@@ -92,6 +92,45 @@
     m_enabled = false;
 }
 
+void InspectorCanvasAgent::requestNode(ErrorString& errorString, const String& canvasId, int* nodeId)
+{
+    const CanvasEntry* canvasEntry = getCanvasEntry(canvasId);
+    if (!canvasEntry) {
+        errorString = ASCIILiteral("Invalid canvas identifier");
+        return;
+    }
+
+    int documentNodeId = m_instrumentingAgents.inspectorDOMAgent()->boundNodeId(&canvasEntry->element->document());
+    if (!documentNodeId) {
+        errorString = ASCIILiteral("Document has not been requested");
+        return;
+    }
+
+    *nodeId = m_instrumentingAgents.inspectorDOMAgent()->pushNodeToFrontend(errorString, documentNodeId, canvasEntry->element);
+}
+
+void InspectorCanvasAgent::requestContent(ErrorString& errorString, const String& canvasId, String* content)
+{
+    const CanvasEntry* canvasEntry = getCanvasEntry(canvasId);
+    if (!canvasEntry) {
+        errorString = ASCIILiteral("Invalid canvas identifier");
+        return;
+    }
+
+    CanvasRenderingContext* context = canvasEntry->element->renderingContext();
+    if (is<CanvasRenderingContext2D>(context)) {
+        ExceptionOr<String> result = canvasEntry->element->toDataURL(ASCIILiteral("image/png"));
+        if (result.hasException()) {
+            errorString = result.releaseException().releaseMessage();
+            return;
+        }
+        *content = result.releaseReturnValue();
+    } else {
+        // FIXME: <https://webkit.org/b/173569> Web Inspector: Support getting the content of WebGL/WebGL2/WebGPU contexts
+        errorString = ASCIILiteral("Unsupported canvas context type");
+    }
+}
+
 void InspectorCanvasAgent::frameNavigated(Frame& frame)
 {
     if (frame.isMainFrame()) {
@@ -105,14 +144,10 @@
             canvasesForFrame.append(canvasElement);
     }
 
-    if (!m_enabled) {
-        m_canvasEntries.clear();
-        return;
-    }
-
     for (auto* canvasElement : canvasesForFrame) {
         auto canvasEntry = m_canvasEntries.take(canvasElement);
-        m_frontendDispatcher->canvasRemoved(canvasEntry.identifier);
+        if (m_enabled)
+            m_frontendDispatcher->canvasRemoved(canvasEntry.identifier);
     }
 }
 
@@ -132,8 +167,7 @@
     }
 
     CanvasEntry newCanvasEntry("canvas:" + IdentifiersFactory::createIdentifier(), &canvasElement);
-    if (m_canvasToCSSCanvasId.contains(&canvasElement))
-        newCanvasEntry.cssCanvasName = m_canvasToCSSCanvasId.take(&canvasElement);
+    newCanvasEntry.cssCanvasName = m_canvasToCSSCanvasId.take(&canvasElement);
 
     m_canvasEntries.set(&canvasElement, newCanvasEntry);
     canvasElement.addObserver(*this);
@@ -229,7 +263,20 @@
 
     if (!canvasEntry.cssCanvasName.isEmpty())
         canvas->setCssCanvasName(canvasEntry.cssCanvasName);
+    else {
+        InspectorDOMAgent* domAgent = m_instrumentingAgents.inspectorDOMAgent();
+        int nodeId = domAgent->boundNodeId(&canvasElement);
+        if (!nodeId) {
+            if (int documentNodeId = domAgent->boundNodeId(&canvasElement.document())) {
+                ErrorString ignored;
+                nodeId = domAgent->pushNodeToFrontend(ignored, documentNodeId, &canvasElement);
+            }
+        }
 
+        if (nodeId)
+            canvas->setNodeId(nodeId);
+    }
+
     return canvas;
 }
 

Modified: trunk/Source/WebCore/inspector/InspectorCanvasAgent.h (218543 => 218544)


--- trunk/Source/WebCore/inspector/InspectorCanvasAgent.h	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebCore/inspector/InspectorCanvasAgent.h	2017-06-20 06:48:10 UTC (rev 218544)
@@ -38,7 +38,6 @@
 
 namespace WebCore {
 
-class DocumentLoader;
 class InspectorPageAgent;
 class WebGLRenderingContextBase;
 
@@ -58,6 +57,8 @@
     // CanvasBackendDispatcherHandler
     void enable(ErrorString&) override;
     void disable(ErrorString&) override;
+    void requestNode(ErrorString&, const String& canvasId, int* nodeId) override;
+    void requestContent(ErrorString&, const String& canvasId, String* content) override;
 
     // InspectorInstrumentation
     void frameNavigated(Frame&);

Modified: trunk/Source/WebInspectorUI/ChangeLog (218543 => 218544)


--- trunk/Source/WebInspectorUI/ChangeLog	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/ChangeLog	2017-06-20 06:48:10 UTC (rev 218544)
@@ -1,5 +1,105 @@
 2017-06-19  Devin Rousso  <drou...@apple.com>
 
+        Web Inspector: create canvas content view and details sidebar panel
+        https://bugs.webkit.org/show_bug.cgi?id=138941
+        <rdar://problem/19051672>
+
+        Reviewed by Joseph Pecoraro.
+
+        * UserInterface/Models/Canvas.js:
+        (WebInspector.Canvas):
+        (WebInspector.Canvas.fromPayload):
+        (WebInspector.Canvas.prototype.get displayName):
+        (WebInspector.Canvas.requestNode): Added.
+        (WebInspector.Canvas.prototype.requestContent): Added.
+        (WebInspector.Canvas.prototype.saveIdentityToCookie):
+
+        * UserInterface/Controllers/CanvasManager.js:
+        (WebInspector.CanvasManager.prototype.canvasAdded):
+        (WebInspector.CanvasManager.prototype.canvasRemoved):
+        * UserInterface/Models/Collection.js:
+        * UserInterface/Models/Frame.js:
+        (WebInspector.Frame):
+        (WebInspector.Frame.prototype.get canvasCollection):
+        (WebInspector.Frame.prototype.commitProvisionalLoad):
+        Create a Collection for Canvas in each Frame, and modify it when canvas events are fired.
+
+        * UserInterface/Views/CanvasContentView.css: Added.
+        (.content-view.canvas > .preview):
+        (.content-view.canvas > .preview > img):
+        * UserInterface/Views/CanvasContentView.js: Added.
+        (WebInspector.CanvasContentView):
+        (WebInspector.CanvasContentView.prototype.get navigationItems):
+        (WebInspector.CanvasContentView.prototype.shown):
+        (WebInspector.CanvasContentView.prototype.hidden):
+        (WebInspector.CanvasContentView.prototype._showPreview):
+        (WebInspector.CanvasContentView.prototype._updateImageGrid):
+        (WebInspector.CanvasContentView.prototype._showGridButtonClicked):
+        * UserInterface/Views/CanvasDetailsSidebarPanel.js: Added.
+        (WebInspector.CanvasDetailsSidebarPanel):
+        (WebInspector.CanvasDetailsSidebarPanel.prototype.inspect):
+        (WebInspector.CanvasDetailsSidebarPanel.prototype.get canvas):
+        (WebInspector.CanvasDetailsSidebarPanel.prototype.set canvas):
+        (WebInspector.CanvasDetailsSidebarPanel.prototype.initialLayout):
+        (WebInspector.CanvasDetailsSidebarPanel.prototype.layout):
+        (WebInspector.CanvasDetailsSidebarPanel.prototype._refreshIdentitySection):
+        (WebInspector.CanvasDetailsSidebarPanel.prototype._refreshSourceSection.this._canvas.requestNode.):
+        (WebInspector.CanvasDetailsSidebarPanel.prototype._refreshSourceSection):
+        * UserInterface/Views/CanvasTreeElement.js: Added.
+        (WebInspector.CanvasTreeElement):
+
+        * UserInterface/Views/FrameTreeElement.js:
+        (WebInspector.FrameTreeElement.prototype.onattach):
+        (WebInspector.FrameTreeElement.prototype.ondetach):
+        (WebInspector.FrameTreeElement.prototype.onpopulate):
+        (WebInspector.FrameTreeElement.prototype._canvasWasAdded):
+        (WebInspector.FrameTreeElement.prototype._canvasWasRemoved):
+        (WebInspector.FrameTreeElement):
+        * UserInterface/Base/Main.js:
+        * UserInterface/Views/ContentView.js:
+        (WebInspector.ContentView.createFromRepresentedObject):
+        (WebInspector.ContentView.isViewable):
+        * UserInterface/Views/ResourceSidebarPanel.js:
+        (WebInspector.ResourceSidebarPanel):
+        (WebInspector.ResourceSidebarPanel.prototype.matchTreeElementAgainstCustomFilters.match):
+        (WebInspector.ResourceSidebarPanel.prototype._treeSelectionDidChange):
+        * UserInterface/Views/ResourcesTabContentView.js:
+        (WebInspector.ResourcesTabContentView):
+        (WebInspector.ResourcesTabContentView.prototype.canShowRepresentedObject):
+        Show Canvas objects and tie them to the correct ContentView and TreeElement subclasses.
+
+        * Localizations/en.lproj/localizedStrings.js:
+        * UserInterface/Main.html:
+        * UserInterface/Images/Canvas.svg: Added.
+        * UserInterface/Views/ResourceIcons.css:
+        (.canvas .icon):
+        Added new files/rules/strings related to Canvas UI.
+
+        * UserInterface/Views/SettingsTabContentView.css:
+        (.content-view.settings .navigation-bar):
+
+        * UserInterface/Base/Setting.js:
+        * UserInterface/Views/SettingsTabContentView.js:
+        (WebInspector.SettingsTabContentView.prototype.initialLayout):
+        (WebInspector.SettingsTabContentView.prototype._createGeneralSettingsView):
+        (WebInspector.SettingsTabContentView.prototype._createDebugSettingsView):
+        Add an experimental settings toggle in the Debug view for showing canvas contexts.
+
+        * UserInterface/Views/FolderizedTreeElement.js:
+        (WebInspector.FolderizedTreeElement.prototype._compareTreeElementsByMainTitle):
+        Drive-by fix: ensure that sorting also includes numbers, so that 1 < 2 < 10.
+
+        * UserInterface/Views/ImageResourceContentView.js:
+        (WebInspector.ImageResourceContentView.prototype.shown):
+        (WebInspector.ImageResourceContentView.prototype.hidden):
+        (WebInspector.ImageResourceContentView.prototype._updateImageGrid):
+        (WebInspector.ImageResourceContentView.prototype._showGridButtonClicked):
+        (WebInspector.ImageResourceContentView):
+        Drive-by fix: change the activated state of the Show Grid navigation item if it changes
+        in another view.
+
+2017-06-19  Devin Rousso  <drou...@apple.com>
+
         Web Inspector: Unify contextmenu items for all node links/previews
         https://bugs.webkit.org/show_bug.cgi?id=173187
 

Modified: trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js (218543 => 218544)


--- trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -55,6 +55,7 @@
 localizedStrings["(uninitialized)"] = "(uninitialized)";
 localizedStrings[", "] = ", ";
 localizedStrings["1 match"] = "1 match";
+localizedStrings["2D"] = "2D";
 localizedStrings["Accessibility"] = "Accessibility";
 localizedStrings["Action"] = "Action";
 localizedStrings["Activity Viewer"] = "Activity Viewer";
@@ -136,6 +137,8 @@
 localizedStrings["Bubbling"] = "Bubbling";
 localizedStrings["Busy"] = "Busy";
 localizedStrings["CSP Hash"] = "CSP Hash";
+localizedStrings["CSS Canvas"] = "CSS Canvas";
+localizedStrings["CSS canvas “%s”"] = "CSS canvas “%s”";
 localizedStrings["Cached"] = "Cached";
 localizedStrings["Call Frames Truncated"] = "Call Frames Truncated";
 localizedStrings["Call Stack"] = "Call Stack";
@@ -143,6 +146,11 @@
 localizedStrings["Calls"] = "Calls";
 localizedStrings["Cancel Automatic Continue"] = "Cancel Automatic Continue";
 localizedStrings["Cancel comparison"] = "Cancel comparison";
+localizedStrings["Canvas"] = "Canvas";
+localizedStrings["Canvas %d"] = "Canvas %d";
+localizedStrings["Canvas %s"] = "Canvas %s";
+localizedStrings["Canvas:"] = "Canvas:";
+localizedStrings["Canvases"] = "Canvases";
 localizedStrings["Cap"] = "Cap";
 localizedStrings["Caps"] = "Caps";
 localizedStrings["Capturing"] = "Capturing";
@@ -262,6 +270,7 @@
 localizedStrings["Delete Breakpoints"] = "Delete Breakpoints";
 localizedStrings["Delete Node"] = "Delete Node";
 localizedStrings["Detach into separate window"] = "Detach into separate window";
+localizedStrings["Detached"] = "Detached";
 localizedStrings["Details"] = "Details";
 localizedStrings["Did you mean “%s”?\nClick to replace."] = "Did you mean “%s”?\nClick to replace.";
 localizedStrings["Dimensions"] = "Dimensions";
@@ -561,6 +570,7 @@
 localizedStrings["No Filter Results"] = "No Filter Results";
 localizedStrings["No Layer Available"] = "No Layer Available";
 localizedStrings["No Parameters"] = "No Parameters";
+localizedStrings["No Preview Available"] = "No Preview Available";
 localizedStrings["No Properties"] = "No Properties";
 localizedStrings["No Properties \u2014 Click to Edit"] = "No Properties \u2014 Click to Edit";
 localizedStrings["No Query Parameters"] = "No Query Parameters";
@@ -751,6 +761,7 @@
 localizedStrings["Show All"] = "Show All";
 localizedStrings["Show All Nodes (%d More)"] = "Show All Nodes (%d More)";
 localizedStrings["Show Console tab"] = "Show Console tab";
+localizedStrings["Show Contexts in Resources Tab"] = "Show Contexts in Resources Tab";
 localizedStrings["Show Grid"] = "Show Grid";
 localizedStrings["Show Remaining (%d)"] = "Show Remaining (%d)";
 localizedStrings["Show Scope Chain on pause"] = "Show Scope Chain on pause";

Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Main.js (218543 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Base/Main.js	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Main.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -1131,7 +1131,8 @@
     if (representedObject instanceof WebInspector.Frame
         || representedObject instanceof WebInspector.Resource
         || representedObject instanceof WebInspector.Script
-        || representedObject instanceof WebInspector.CSSStyleSheet)
+        || representedObject instanceof WebInspector.CSSStyleSheet
+        || representedObject instanceof WebInspector.Canvas)
         return WebInspector.ResourcesTabContentView;
 
     // FIXME: Move Content Flows to the Elements tab?

Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Setting.js (218543 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Base/Setting.js	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Setting.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -121,4 +121,7 @@
     stylesSelectOnFirstClick: new WebInspector.Setting("styles-select-on-first-click", true),
     showScopeChainOnPause: new WebInspector.Setting("show-scope-chain-sidebar", true),
     showImageGrid: new WebInspector.Setting("show-image-grid", false),
+
+    // Experimental
+    experimentalShowCanvasContextsInResources: new WebInspector.Setting("experimental-show-canvas-contexts-in-resources", false),
 };

Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/CanvasManager.js (218543 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Controllers/CanvasManager.js	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/CanvasManager.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -53,6 +53,8 @@
         let canvas = WebInspector.Canvas.fromPayload(canvasPayload);
         this._canvasIdentifierMap.set(canvas.identifier, canvas);
 
+        canvas.frame.canvasCollection.add(canvas);
+
         this.dispatchEventToListeners(WebInspector.CanvasManager.Event.CanvasWasAdded, {canvas});
     }
 
@@ -65,6 +67,8 @@
         if (!canvas)
             return;
 
+        canvas.frame.canvasCollection.remove(canvas);
+
         this.dispatchEventToListeners(WebInspector.CanvasManager.Event.CanvasWasRemoved, {canvas});
     }
 

Added: trunk/Source/WebInspectorUI/UserInterface/Images/Canvas.svg (0 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Images/Canvas.svg	                        (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Images/Canvas.svg	2017-06-20 06:48:10 UTC (rev 218544)
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2017 Apple Inc. All rights reserved. -->
+<svg viewBox="0 0 14 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
+    <path fill="rgb(92, 140, 229)" stroke="white" stroke-width="0.5" stroke-linejoin="round" stroke-linecap="square" d="M 0.5 4.29857047 L 7.09714095 7.59714095 L 7.09714095 15.0189245 L 0.5 11.720354 L 0.5 4.29857047 Z" />
+    <path fill="rgb(242, 97, 97)" stroke="white" stroke-width="0.5" stroke-linejoin="round" stroke-linecap="square" d="M 13.697 4.299 L 7.097 7.597 L 7.097 15.019 L 13.697 11.72 L 13.697 4.299 Z"/>
+    <path fill="rgb(97, 242, 97)" stroke="white" stroke-width="0.5" stroke-linejoin="round" d="M 0.5 4.29857047 L 7.09714095 1 L 13.6908901 4.29857047 L 7.09714095 7.59714095 L 0.5 4.29857047 Z"/>
+</svg>

Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (218543 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Main.html	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html	2017-06-20 06:48:10 UTC (rev 218544)
@@ -45,6 +45,7 @@
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
+    <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
@@ -508,6 +509,9 @@
     <script src=""
     <script src=""
     <script src=""
+    <script src=""
+    <script src=""
+    <script src=""
     <script src=""
     <script src=""
     <script src=""

Modified: trunk/Source/WebInspectorUI/UserInterface/Models/Canvas.js (218543 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Models/Canvas.js	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/Canvas.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -25,7 +25,7 @@
 
 WebInspector.Canvas = class Canvas extends WebInspector.Object
 {
-    constructor(identifier, contextType, frame, cssCanvasName)
+    constructor(identifier, contextType, frame, {domNode, cssCanvasName} = {})
     {
         super();
 
@@ -36,6 +36,7 @@
         this._identifier = identifier;
         this._contextType = contextType;
         this._frame = frame;
+        this._domNode = domNode || null;
         this._cssCanvasName = cssCanvasName || "";
     }
 
@@ -56,7 +57,10 @@
         }
 
         let frame = WebInspector.frameResourceManager.frameForIdentifier(payload.frameId);
-        return new WebInspector.Canvas(payload.canvasId, contextType, frame, payload.cssCanvasName);
+        return new WebInspector.Canvas(payload.canvasId, contextType, frame, {
+            domNode: payload.nodeId ? WebInspector.domTreeManager.nodeForId(payload.nodeId) : null,
+            cssCanvasName: payload.cssCanvasName,
+        });
     }
 
     static displayNameForContextType(contextType)
@@ -65,7 +69,7 @@
         case WebInspector.Canvas.ContextType.Canvas2D:
             return WebInspector.UIString("2D");
         case WebInspector.Canvas.ContextType.WebGL:
-            return WebInspector.UIString("WebGL");
+            return WebInspector.unlocalizedString("WebGL");
         default:
             console.error("Invalid canvas context type", contextType);
         }
@@ -85,26 +89,61 @@
 
     get displayName()
     {
-        if (this.cssCanvasName) {
-            console.assert(!this._node, "Unexpected DOM node for CSS canvas.");
+        if (this._cssCanvasName)
             return WebInspector.UIString("CSS canvas “%s”").format(this._cssCanvasName);
+
+        if (this._domNode) {
+            let idSelector = this._domNode.escapedIdSelector;
+            if (idSelector)
+                return WebInspector.UIString("Canvas %s").format(idSelector);
         }
 
-        // TODO:if the DOM node for the canvas is known and an id attribute value
-        // exists, return the following: WebInspector.UIString("Canvas #%s").format(id);
-
         if (!this._uniqueDisplayNameNumber)
             this._uniqueDisplayNameNumber = this.constructor._nextUniqueDisplayNameNumber++;
         return WebInspector.UIString("Canvas %d").format(this._uniqueDisplayNameNumber);
     }
 
+    requestNode(callback)
+    {
+        if (this._domNode) {
+            callback(this._domNode);
+            return;
+        }
+
+        WebInspector.domTreeManager.requestDocument((document) => {
+            CanvasAgent.requestNode(this._identifier, (error, nodeId) => {
+                if (error) {
+                    callback(null);
+                    return;
+                }
+
+                this._domNode = WebInspector.domTreeManager.nodeForId(nodeId);
+                callback(this._domNode);
+            });
+        });
+    }
+
+    requestContent(callback)
+    {
+        CanvasAgent.requestContent(this._identifier, (error, content) => {
+            if (error) {
+                callback(null);
+                return;
+            }
+
+            callback(content);
+        });
+    }
+
     saveIdentityToCookie(cookie)
     {
         cookie[WebInspector.Canvas.FrameURLCookieKey] = this._frame.url.hash;
+
         if (this._cssCanvasName)
             cookie[WebInspector.Canvas.CSSCanvasNameCookieKey] = this._cssCanvasName;
+        else if (this._domNode)
+            cookie[WebInspector.Canvas.NodePathCookieKey] = this._domNode.path;
 
-        // TODO: if the canvas has an associated DOM node, and the node path to the cookie.
     }
 };
 
@@ -114,6 +153,8 @@
 WebInspector.Canvas.CSSCanvasNameCookieKey = "canvas-css-canvas-name";
 
 WebInspector.Canvas.ContextType = {
-    Canvas2D: Symbol("canvas-2d"),
-    WebGL: Symbol("webgl"),
+    Canvas2D: "canvas-2d",
+    WebGL: "webgl",
 };
+
+WebInspector.Canvas.ResourceSidebarType = "resource-type-canvas";

Modified: trunk/Source/WebInspectorUI/UserInterface/Models/Collection.js (218543 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Models/Collection.js	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/Collection.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -117,4 +117,5 @@
     Resource: (object) => object instanceof WebInspector.Resource,
     Script: (object) => object instanceof WebInspector.Script,
     CSSStyleSheet: (object) => object instanceof WebInspector.CSSStyleSheet,
+    Canvas: (object) => object instanceof WebInspector.Canvas,
 };

Modified: trunk/Source/WebInspectorUI/UserInterface/Models/Frame.js (218543 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Models/Frame.js	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/Frame.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -39,6 +39,7 @@
         this._resourceCollection = new WebInspector.ResourceCollection;
         this._provisionalResourceCollection = new WebInspector.ResourceCollection;
         this._extraScriptCollection = new WebInspector.Collection(WebInspector.Collection.TypeVerifier.Script);
+        this._canvasCollection = new WebInspector.Collection(WebInspector.Collection.TypeVerifier.Canvas);
 
         this._childFrameCollection = new WebInspector.Collection(WebInspector.Collection.TypeVerifier.Frame);
         this._childFrameIdentifierMap = new Map;
@@ -58,6 +59,7 @@
 
     get resourceCollection() { return this._resourceCollection; }
     get extraScriptCollection() { return this._extraScriptCollection; }
+    get canvasCollection() { return this._canvasCollection; }
     get childFrameCollection() { return this._childFrameCollection; }
 
     initialize(name, securityOrigin, loaderIdentifier, mainResource)
@@ -133,6 +135,7 @@
         this._resourceCollection = this._provisionalResourceCollection;
         this._provisionalResourceCollection = new WebInspector.ResourceCollection;
         this._extraScriptCollection.clear();
+        this._canvasCollection.clear();
 
         this.clearExecutionContexts(true);
         this.clearProvisionalLoad(true);

Added: trunk/Source/WebInspectorUI/UserInterface/Views/CanvasContentView.css (0 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Views/CanvasContentView.css	                        (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CanvasContentView.css	2017-06-20 06:48:10 UTC (rev 218544)
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.content-view.canvas > .preview {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    width: 100%;
+    height: 100%;
+    padding: 15px;
+}
+
+.content-view.canvas > .preview > img {
+    max-width: 100%;
+    max-height: 100%;
+}

Added: trunk/Source/WebInspectorUI/UserInterface/Views/CanvasContentView.js (0 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Views/CanvasContentView.js	                        (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CanvasContentView.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CanvasContentView = class CanvasContentView extends WebInspector.ContentView
+{
+    constructor(representedObject)
+    {
+        console.assert(representedObject instanceof WebInspector.Canvas);
+
+        super(representedObject);
+
+        this.element.classList.add("canvas");
+
+        this._previewContainerElement = null;
+        this._previewImageElement = null;
+        this._errorElement = null;
+
+        this._refreshButtonNavigationItem = new WebInspector.ButtonNavigationItem("canvas-refresh", WebInspector.UIString("Refresh"), "Images/ReloadFull.svg", 13, 13);
+        this._refreshButtonNavigationItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._showPreview, this);
+
+        this._showGridButtonNavigationItem = new WebInspector.ActivateButtonNavigationItem("show-grid", WebInspector.UIString("Show Grid"), WebInspector.UIString("Hide Grid"), "Images/NavigationItemCheckers.svg", 13, 13);
+        this._showGridButtonNavigationItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._showGridButtonClicked, this);
+        this._showGridButtonNavigationItem.activated = !!WebInspector.settings.showImageGrid.value;
+    }
+
+    // Protected
+
+    get navigationItems()
+    {
+        return [this._refreshButtonNavigationItem, this._showGridButtonNavigationItem];
+    }
+
+    shown()
+    {
+        super.shown();
+
+        this._showPreview();
+
+        WebInspector.settings.showImageGrid.addEventListener(WebInspector.Setting.Event.Changed, this._updateImageGrid, this);
+    }
+
+    hidden()
+    {
+        WebInspector.settings.showImageGrid.removeEventListener(WebInspector.Setting.Event.Changed, this._updateImageGrid, this);
+
+        super.hidden();
+    }
+
+    // Private
+
+    _showPreview()
+    {
+        let showError = () => {
+            if (this._previewContainerElement)
+                this._previewContainerElement.remove();
+
+            if (!this._errorElement) {
+                const isError = true;
+                this._errorElement = WebInspector.createMessageTextView(WebInspector.UIString("No Preview Available"), isError);
+            }
+
+            this.element.appendChild(this._errorElement);
+        };
+
+        if (!this._previewContainerElement) {
+            this._previewContainerElement = this.element.appendChild(document.createElement("div"));
+            this._previewContainerElement.classList.add("preview");
+        }
+
+        this.representedObject.requestContent((content) => {
+            if (!content) {
+                showError();
+                return;
+            }
+
+            if (this._errorElement)
+                this._errorElement.remove();
+
+            if (!this._previewImageElement) {
+                this._previewImageElement = document.createElement("img");
+                this._previewImageElement.addEventListener("error", showError);
+            }
+
+            this._previewImageElement.src = ""
+            this._previewContainerElement.appendChild(this._previewImageElement);
+
+            this._updateImageGrid();
+        });
+    }
+
+    _updateImageGrid()
+    {
+        if (!this._previewImageElement)
+            return;
+
+        let activated = WebInspector.settings.showImageGrid.value;
+        this._showGridButtonNavigationItem.activated = activated;
+        this._previewImageElement.classList.toggle("show-grid", activated);
+    }
+
+    _showGridButtonClicked(event)
+    {
+        WebInspector.settings.showImageGrid.value = !this._showGridButtonNavigationItem.activated;
+
+        this._updateImageGrid();
+    }
+};

Added: trunk/Source/WebInspectorUI/UserInterface/Views/CanvasDetailsSidebarPanel.js (0 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Views/CanvasDetailsSidebarPanel.js	                        (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CanvasDetailsSidebarPanel.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends WebInspector.DetailsSidebarPanel
+{
+    constructor()
+    {
+        super("canvas-details", WebInspector.UIString("Canvas"));
+
+        this.element.classList.add("canvas");
+
+        this._canvas = null;
+        this._node = null;
+    }
+
+    // Public
+
+    inspect(objects)
+    {
+        if (!(objects instanceof Array))
+            objects = [objects];
+
+        this.canvas = objects.find((object) => object instanceof WebInspector.Canvas);
+
+        return !!this._canvas;
+    }
+
+    get canvas()
+    {
+        return this._canvas;
+    }
+
+    set canvas(canvas)
+    {
+        if (canvas === this._canvas)
+            return;
+
+        this._canvas = canvas || null;
+
+        if (this._node) {
+            this._node.removeEventListener(WebInspector.DOMNode.Event.AttributeModified, this._refreshSourceSection, this);
+            this._node.removeEventListener(WebInspector.DOMNode.Event.AttributeRemoved, this._refreshSourceSection, this);
+
+            this._node = null;
+        }
+
+        this.needsLayout();
+    }
+
+    // Protected
+
+    initialLayout()
+    {
+        super.initialLayout();
+
+        this._nameRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Name"));
+        this._typeRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Type"));
+
+        let identitySection = new WebInspector.DetailsSection("canvas-details", WebInspector.UIString("Identity"));
+        identitySection.groups = [new WebInspector.DetailsSectionGroup([this._nameRow, this._typeRow])];
+        this.contentView.element.appendChild(identitySection.element);
+
+        this._nodeRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Node"));
+        this._cssCanvasRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("CSS Canvas"));
+        this._widthRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Width"));
+        this._heightRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Height"));
+        this._datachedRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Detached"));
+
+        let sourceSection = new WebInspector.DetailsSection("canvas-source", WebInspector.UIString("Source"));
+        sourceSection.groups = [new WebInspector.DetailsSectionGroup([this._nodeRow, this._cssCanvasRow, this._widthRow, this._heightRow, this._datachedRow])];
+        this.contentView.element.appendChild(sourceSection.element);
+    }
+
+    layout()
+    {
+        super.layout();
+
+        if (!this._canvas)
+            return;
+
+        this._refreshIdentitySection();
+        this._refreshSourceSection();
+    }
+
+    // Private
+
+    _refreshIdentitySection()
+    {
+        if (!this._canvas)
+            return;
+
+        this._nameRow.value = this._canvas.displayName;
+        this._typeRow.value = WebInspector.Canvas.displayNameForContextType(this._canvas.contextType);
+    }
+
+    _refreshSourceSection()
+    {
+        if (!this._canvas)
+            return;
+
+        this._nodeRow.value = this._canvas.cssCanvasName ? null : emDash;
+        this._cssCanvasRow.value = this._canvas.cssCanvasName || null;
+        this._widthRow.value = emDash;
+        this._heightRow.value = emDash;
+        this._datachedRow.value = null;
+
+        this._canvas.requestNode((node) => {
+            if (!node)
+                return;
+
+            if (node !== this._node) {
+                if (this._node) {
+                    this._node.removeEventListener(WebInspector.DOMNode.Event.AttributeModified, this._refreshSourceSection, this);
+                    this._node.removeEventListener(WebInspector.DOMNode.Event.AttributeRemoved, this._refreshSourceSection, this);
+
+                    this._node = null;
+                }
+
+                this._node = node;
+
+                this._node.addEventListener(WebInspector.DOMNode.Event.AttributeModified, this._refreshSourceSection, this);
+                this._node.addEventListener(WebInspector.DOMNode.Event.AttributeRemoved, this._refreshSourceSection, this);
+            }
+
+            if (!this._canvas.cssCanvasName)
+                this._nodeRow.value = WebInspector.linkifyNodeReference(this._node);
+
+            let setRowValueIfValidAttributeValue = (row, attribute) => {
+                let value = Number(this._node.getAttribute(attribute));
+                if (!Number.isInteger(value) || value < 0)
+                    return false;
+
+                row.value = value;
+                return true;
+            };
+
+            let validWidth = setRowValueIfValidAttributeValue(this._widthRow, "width");
+            let validHeight = setRowValueIfValidAttributeValue(this._heightRow, "height");
+            if (!validWidth || !validHeight) {
+                // Since the "width" and "height" properties of canvas elements are more than just
+                // attributes, we need to invoke the getter for each to get the actual value.
+                //  - https://html.spec.whatwg.org/multipage/canvas.html#attr-canvas-width
+                //  - https://html.spec.whatwg.org/multipage/canvas.html#attr-canvas-height
+                WebInspector.RemoteObject.resolveNode(node, "", (remoteObject) => {
+                    if (!remoteObject)
+                        return;
+
+                    function setRowValueToPropertyValue(row, property) {
+                        remoteObject.getProperty(property, (error, result, wasThrown) => {
+                            if (!error && result.type === "number")
+                                row.value = `${result.value}px`;
+                        });
+                    }
+
+                    setRowValueToPropertyValue(this._widthRow, "width");
+                    setRowValueToPropertyValue(this._heightRow, "height");
+
+                    remoteObject.release();
+                });
+            }
+
+            if (!this._canvas.cssCanvasName && !this._node.parentNode)
+                this._datachedRow.value = WebInspector.UIString("Yes");
+        });
+    }
+};

Added: trunk/Source/WebInspectorUI/UserInterface/Views/CanvasTreeElement.js (0 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Views/CanvasTreeElement.js	                        (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CanvasTreeElement.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CanvasTreeElement = class CanvasTreeElement extends WebInspector.GeneralTreeElement
+{
+    constructor(representedObject)
+    {
+        console.assert(representedObject instanceof WebInspector.Canvas);
+
+        const subtitle = null;
+        super(["canvas", representedObject.contextType], representedObject.displayName, subtitle, representedObject);
+    }
+};

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ContentView.js (218543 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Views/ContentView.js	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ContentView.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -57,6 +57,9 @@
         if (representedObject instanceof WebInspector.CSSStyleSheet)
             return new WebInspector.TextResourceContentView(representedObject, extraArguments);
 
+        if (representedObject instanceof WebInspector.Canvas)
+            return new WebInspector.CanvasContentView(representedObject, extraArguments);
+
         if (representedObject instanceof WebInspector.TimelineRecording)
             return new WebInspector.TimelineRecordingContentView(representedObject, extraArguments);
 
@@ -239,6 +242,8 @@
             return true;
         if (representedObject instanceof WebInspector.CSSStyleSheet)
             return true;
+        if (representedObject instanceof WebInspector.Canvas)
+            return true;
         if (representedObject instanceof WebInspector.TimelineRecording)
             return true;
         if (representedObject instanceof WebInspector.Timeline)

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/FolderizedTreeElement.js (218543 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Views/FolderizedTreeElement.js	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/FolderizedTreeElement.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -237,7 +237,7 @@
             return 1;
 
         // Then sort by title.
-        return a.mainTitle.localeCompare(b.mainTitle);
+        return a.mainTitle.localeCompare(b.mainTitle, undefined, {numeric: true});
     }
 
     _insertFolderTreeElement(folderTreeElement)

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/FrameTreeElement.js (218543 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Views/FrameTreeElement.js	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/FrameTreeElement.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -53,6 +53,9 @@
         this.registerFolderizeSettings("flows", WebInspector.UIString("Flows"), this._frame.domTree.contentFlowCollection, WebInspector.ContentFlowTreeElement);
         this.registerFolderizeSettings("extra-scripts", WebInspector.UIString("Extra Scripts"), this._frame.extraScriptCollection, WebInspector.ScriptTreeElement);
 
+        if (window.CanvasAgent && WebInspector.settings.experimentalShowCanvasContextsInResources.value)
+            this.registerFolderizeSettings("canvases", WebInspector.UIString("Canvases"), this._frame.canvasCollection, WebInspector.CanvasTreeElement);
+
         function forwardingConstructor(representedObject, ...extraArguments) {
             if (representedObject instanceof WebInspector.CSSStyleSheet)
                 return new WebInspector.CSSStyleSheetTreeElement(representedObject, ...extraArguments);
@@ -121,6 +124,11 @@
         WebInspector.GeneralTreeElement.prototype.onattach.call(this);
 
         WebInspector.cssStyleManager.addEventListener(WebInspector.CSSStyleManager.Event.StyleSheetAdded, this._styleSheetAdded, this);
+
+        if (window.CanvasAgent && WebInspector.settings.experimentalShowCanvasContextsInResources.value) {
+            this._frame.canvasCollection.addEventListener(WebInspector.Collection.Event.ItemAdded, this._canvasWasAdded, this);
+            this._frame.canvasCollection.addEventListener(WebInspector.Collection.Event.ItemRemoved, this._canvasWasRemoved, this);
+        }
     }
 
     ondetach()
@@ -127,6 +135,11 @@
     {
         WebInspector.cssStyleManager.removeEventListener(WebInspector.CSSStyleManager.Event.StyleSheetAdded, this._styleSheetAdded, this);
 
+        if (window.CanvasAgent && WebInspector.settings.experimentalShowCanvasContextsInResources.value) {
+            this._frame.canvasCollection.removeEventListener(WebInspector.Collection.Event.ItemAdded, this._canvasWasAdded, this);
+            this._frame.canvasCollection.removeEventListener(WebInspector.Collection.Event.ItemRemoved, this._canvasWasRemoved, this);
+        }
+
         super.ondetach();
     }
 
@@ -186,6 +199,11 @@
                 this.addChildForRepresentedObject(extraScript);
         }
 
+        if (window.CanvasAgent && WebInspector.settings.experimentalShowCanvasContextsInResources.value) {
+            for (let canvas of this._frame.canvasCollection.items)
+                this.addChildForRepresentedObject(canvas);
+        }
+
         const doNotCreateIfMissing = true;
         WebInspector.cssStyleManager.preferredInspectorStyleSheetForFrame(this._frame, this.addRepresentedObjectToNewChildQueue.bind(this), doNotCreateIfMissing);
     }
@@ -279,4 +297,14 @@
 
         this.addRepresentedObjectToNewChildQueue(event.data.styleSheet);
     }
+
+    _canvasWasAdded(event)
+    {
+        this.addRepresentedObjectToNewChildQueue(event.data.item);
+    }
+
+    _canvasWasRemoved(event)
+    {
+        this.removeChildForRepresentedObject(event.data.item);
+    }
 };

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.js (218543 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.js	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -59,7 +59,7 @@
         this._imageElement.addEventListener("load", function() { URL.revokeObjectURL(objectURL); });
         this._imageElement.src = ""
         this._imageElement.setAttribute("filename", this.resource.urlComponents.lastPathComponent || "");
-        this._toggleImageGrid();
+        this._updateImageGrid();
 
         this.element.appendChild(this._imageElement);
     }
@@ -70,25 +70,34 @@
     {
         super.shown();
 
-        this._toggleImageGrid();
+        this._updateImageGrid();
+
+        WebInspector.settings.showImageGrid.addEventListener(WebInspector.Setting.Event.Changed, this._updateImageGrid, this);
     }
 
+    hidden()
+    {
+        WebInspector.settings.showImageGrid.removeEventListener(WebInspector.Setting.Event.Changed, this._updateImageGrid, this);
+
+        super.hidden();
+    }
+
     // Private
 
-    _toggleImageGrid()
+    _updateImageGrid()
     {
         if (!this._imageElement)
             return;
 
-        let activated = this._showGridButtonNavigationItem.activated;
-        
+        let activated = WebInspector.settings.showImageGrid.value;
+        this._showGridButtonNavigationItem.activated = activated;
         this._imageElement.classList.toggle("show-grid", activated);
     }
 
     _showGridButtonClicked(event)
     {
-        WebInspector.settings.showImageGrid.value = this._showGridButtonNavigationItem.activated = !this._showGridButtonNavigationItem.activated;
+        WebInspector.settings.showImageGrid.value = !this._showGridButtonNavigationItem.activated;
 
-        this._toggleImageGrid();
+        this._updateImageGrid();
     }
 };

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ResourceIcons.css (218543 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Views/ResourceIcons.css	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ResourceIcons.css	2017-06-20 06:48:10 UTC (rev 218544)
@@ -71,6 +71,10 @@
     content: image-set(url(../Images/WebSocket.png) 1x, url(../Images/websoc...@2x.png) 2x);
 }
 
+.canvas .icon {
+    content: url(../Images/Canvas.svg);
+}
+
 .large .resource-icon .icon {
     content: image-set(url(../Images/DocumentGenericLarge.png) 1x, url(../Images/documentgenericla...@2x.png) 2x);
 }

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ResourceSidebarPanel.js (218543 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Views/ResourceSidebarPanel.js	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ResourceSidebarPanel.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -50,6 +50,10 @@
             scopeBarItems.push(scopeBarItem);
         }
 
+        let canvasesScopeBarItem = new WebInspector.ScopeBarItem(scopeItemPrefix + WebInspector.Canvas.ResourceSidebarType, WebInspector.UIString("Canvases"));
+        canvasesScopeBarItem[WebInspector.ResourceSidebarPanel.ResourceTypeSymbol] = WebInspector.Canvas.ResourceSidebarType;
+        scopeBarItems.insertAtIndex(canvasesScopeBarItem, scopeBarItems.length - 1);
+
         this._scopeBar = new WebInspector.ScopeBar("resource-sidebar-scope-bar", scopeBarItems, scopeBarItems[0], true);
         this._scopeBar.addEventListener(WebInspector.ScopeBar.Event.SelectionChanged, this._scopeBarSelectionDidChange, this);
 
@@ -225,6 +229,9 @@
             if (treeElement instanceof WebInspector.ScriptTreeElement)
                 return selectedScopeBarItem[WebInspector.ResourceSidebarPanel.ResourceTypeSymbol] === WebInspector.Resource.Type.Script;
 
+            if (treeElement instanceof WebInspector.CanvasTreeElement)
+                return selectedScopeBarItem[WebInspector.ResourceSidebarPanel.ResourceTypeSymbol] === WebInspector.Canvas.ResourceSidebarType;
+
             console.assert(treeElement instanceof WebInspector.ResourceTreeElement, "Unknown treeElement", treeElement);
             if (!(treeElement instanceof WebInspector.ResourceTreeElement))
                 return false;
@@ -445,7 +452,8 @@
             || treeElement instanceof WebInspector.ResourceTreeElement
             || treeElement instanceof WebInspector.ScriptTreeElement
             || treeElement instanceof WebInspector.CSSStyleSheetTreeElement
-            || treeElement instanceof WebInspector.ContentFlowTreeElement) {
+            || treeElement instanceof WebInspector.ContentFlowTreeElement
+            || treeElement instanceof WebInspector.CanvasTreeElement) {
             const cookie = null;
             const options = {
                 ignoreNetworkTab: true,

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ResourcesTabContentView.js (218543 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Views/ResourcesTabContentView.js	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ResourcesTabContentView.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -31,6 +31,9 @@
         let tabBarItem = new WebInspector.GeneralTabBarItem(image, title);
         let detailsSidebarPanelConstructors = [WebInspector.ResourceDetailsSidebarPanel, WebInspector.ProbeDetailsSidebarPanel];
 
+        if (window.CanvasAgent && WebInspector.settings.experimentalShowCanvasContextsInResources.value)
+            detailsSidebarPanelConstructors.push(WebInspector.CanvasDetailsSidebarPanel);
+
         // FIXME: Until ContentFlows are moved to the Elements tab, these details sidebar panels need to be included.
         detailsSidebarPanelConstructors = detailsSidebarPanelConstructors.concat([WebInspector.DOMNodeDetailsSidebarPanel, WebInspector.CSSStyleDetailsSidebarPanel]);
         if (window.LayerTreeAgent)
@@ -66,6 +69,7 @@
             || representedObject instanceof WebInspector.Script
             || representedObject instanceof WebInspector.CSSStyleSheet
             || representedObject instanceof WebInspector.ContentFlow
+            || representedObject instanceof WebInspector.Canvas
             || representedObject instanceof WebInspector.Collection;
     }
 };

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.css (218543 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.css	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.css	2017-06-20 06:48:10 UTC (rev 218544)
@@ -29,6 +29,12 @@
     overflow-y: auto;
 }
 
+.content-view.settings .navigation-bar {
+    position: -webkit-sticky;
+    top: 0;
+    background-color: white;
+}
+
 .content-view.settings .navigation-bar.invisible {
     visibility: hidden;
 }

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.js (218543 => 218544)


--- trunk/Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.js	2017-06-20 06:45:32 UTC (rev 218543)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.js	2017-06-20 06:48:10 UTC (rev 218544)
@@ -164,6 +164,8 @@
 
         WebInspector.notifications.addEventListener(WebInspector.Notification.DebugUIEnabledDidChange, this._updateDebugSettingsViewVisibility, this);
         this._updateDebugSettingsViewVisibility();
+
+        this.selectedSettingsView = this._settingsViews[0];
     }
 
     // Private
@@ -222,7 +224,6 @@
         WebInspector.settings.zoomFactor.addEventListener(WebInspector.Setting.Event.Changed, () => { zoomEditor.value = WebInspector.getZoomFactor().maxDecimals(2); });
 
         this.addSettingsView(generalSettingsView);
-        this.selectedSettingsView = generalSettingsView;
     }
 
     _createDebugSettingsView()
@@ -259,6 +260,12 @@
         layoutDirectionEditor.value = WebInspector.settings.layoutDirection.value;
         layoutDirectionEditor.addEventListener(WebInspector.SettingEditor.Event.ValueDidChange, () => { WebInspector.setLayoutDirection(layoutDirectionEditor.value); });
 
+        if (window.CanvasAgent) {
+            this._debugSettingsView.addSeparator();
+
+            this._debugSettingsView.addSetting(WebInspector.UIString("Canvas:"), WebInspector.settings.experimentalShowCanvasContextsInResources, WebInspector.UIString("Show Contexts in Resources Tab"));
+        }
+
         this.addSettingsView(this._debugSettingsView);
     }
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to