Title: [272197] trunk
Revision
272197
Author
[email protected]
Date
2021-02-02 01:02:31 -0800 (Tue, 02 Feb 2021)

Log Message

Web Inspector: implement the basics for showing/hiding grid overlays
https://bugs.webkit.org/show_bug.cgi?id=221062

Reviewed by Devin Rousso.

Source/_javascript_Core:

Add new commands to show and hide CSS grid overlays:

    - DOM.showGridOverlay
    - DOM.hideGridOverlay

* inspector/protocol/DOM.json:

Source/WebCore:

Implement backend commands for showing and hiding CSS grid overlays.
This patch draws a very simplistic grid overlay. Support for the
various grid overlay options will be added in later patches.

New test: inspector/dom/showGridOverlay.html

* inspector/InspectorOverlay.h:
(WebCore::InspectorOverlay::gridOverlayCount const):
Added, for testing only.

* inspector/InspectorOverlay.cpp:
(WebCore::InspectorOverlay::paint):
(WebCore::InspectorOverlay::shouldShowOverlay const):
Hook up the painting of any active grid overlays.

(WebCore::InspectorOverlay::setGridOverlayForNode):
(WebCore::InspectorOverlay::clearGridOverlayForNode):
(WebCore::InspectorOverlay::clearAllGridOverlays):
Maintain the list of active grid overlays. A node can only
have one grid overlay at a time.

(WebCore::InspectorOverlay::drawNodeHighlight):
Add a note about why grid overlays are not considered when
calculating the ruler exclusion area.

(WebCore::InspectorOverlay::drawGridOverlay): Added.
This drawing code exists to flesh out the rest of this patch,
and is obviously incomplete.

Draw grid lines that extend to the edge of the viewport,
equivalent to `config.showExtendedGridLines = true`.

* inspector/agents/InspectorDOMAgent.h:
* inspector/agents/InspectorDOMAgent.cpp:
(WebCore::InspectorDOMAgent::showGridOverlay):
(WebCore::InspectorDOMAgent::hideGridOverlay):
Translate protocol commands into InspectorOverlay method calls.

* inspector/InspectorController.h:
* inspector/InspectorController.cpp:
(WebCore::InspectorController::gridOverlayCount const):
Added. This is used for testing purposes only.

* testing/Internals.h:
* testing/Internals.idl:
* testing/Internals.cpp:
(WebCore::Internals::inspectorGridOverlayCount): Added.

Source/WebInspectorUI:

Expose new DOM.showGridOverlay and DOM.hideGridOverlay commands
via WI.DOMNode. Add initial engineering UI to toggle grid overlays.

New methods are covered by a new test:

    inspector/dom/showGridOverlay.html

Additions to WI.DOMManager.prototype.requestDocument are covered
by existing tests (callback case) and a new test (promise case).

Additions to WI.Color are covered by a new test case in:

    inspector/model/color.html

* UserInterface/Controllers/DOMManager.js:
(WI.DOMManager.prototype.requestDocument):
(WI.DOMManager.prototype._requestDocumentWithPromise):
Drive-by: upgrade requestDocument() to return a promise if
no callback argument was passed. This is used by a new test.

* UserInterface/Models/Color.js:
(WI.Color.prototype.toProtocol): Added. The protocol object is
DOM.RGBAColor, which is the same thing as WI.Color.Format.RGBA.

* UserInterface/Models/DOMNode.js:
(WI.DOMNode.prototype.showGridOverlay):
(WI.DOMNode.prototype.hideGridOverlay):
Added. These are the methods that the rest of WebInspectorUI uses
to interact with grid overlays for a particular node. Note that
these methods return either an unsettled promise (from the agent call)
or a rejected promise (in the case that the node is destroyed).
This allows test cases to `await` before checking the grid overlay count.

* UserInterface/Test/TestHarness.js:
(TestHarness.prototype.expectException): Improve logging output
when InspectorTest.expectException does not catch an exception.

* UserInterface/Views/ContextMenuUtilities.js:
Add some engineering-only context menu items for showing/hiding
grid overlays to DOMTreeElements in the Elements Tab.

These are in place for development purposes and should eventually
be removed when no longer needed.

LayoutTests:

* inspector/dom/showGridOverlay-expected.txt: Added.
* inspector/dom/showGridOverlay.html: Added.
This test does not cover the actual drawing code. The drawing method
will change a lot and is not easy to test currently. The new test
covers the behavior of showGridOverlay/hideGridOverlay by querying
how many grid overlays are active according to InspectorOverlay.

* inspector/model/color-expected.txt:
* inspector/model/color.html:
Add a test case to exercise WI.Color.prototype.toProtocol().

* inspector/unit-tests/test-harness-expect-functions-async-expected.txt:
Rebaseline results after adding more state dumping in the failure case
for InspectorTest.expectException().

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (272196 => 272197)


--- trunk/LayoutTests/ChangeLog	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/LayoutTests/ChangeLog	2021-02-02 09:02:31 UTC (rev 272197)
@@ -1,3 +1,25 @@
+2021-02-02  BJ Burg  <[email protected]>
+
+        Web Inspector: implement the basics for showing/hiding grid overlays
+        https://bugs.webkit.org/show_bug.cgi?id=221062
+
+        Reviewed by Devin Rousso.
+
+        * inspector/dom/showGridOverlay-expected.txt: Added.
+        * inspector/dom/showGridOverlay.html: Added.
+        This test does not cover the actual drawing code. The drawing method
+        will change a lot and is not easy to test currently. The new test
+        covers the behavior of showGridOverlay/hideGridOverlay by querying
+        how many grid overlays are active according to InspectorOverlay.
+
+        * inspector/model/color-expected.txt:
+        * inspector/model/color.html:
+        Add a test case to exercise WI.Color.prototype.toProtocol().
+
+        * inspector/unit-tests/test-harness-expect-functions-async-expected.txt:
+        Rebaseline results after adding more state dumping in the failure case
+        for InspectorTest.expectException().
+
 2021-02-01  Lauro Moura  <[email protected]>
 
         [GLIB] Gardening crashes common to both glib ports

Added: trunk/LayoutTests/inspector/dom/showGridOverlay-expected.txt (0 => 272197)


--- trunk/LayoutTests/inspector/dom/showGridOverlay-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/inspector/dom/showGridOverlay-expected.txt	2021-02-02 09:02:31 UTC (rev 272197)
@@ -0,0 +1,63 @@
+Tests for the DOM.showGridOverlay command.
+
+A
+B
+C
+D
+E
+F
+
+A
+B
+C
+D
+E
+F
+
+== Running test suite: DOM.showGridOverlay
+-- Running test case: DOM.showGridOverlay.ShowOneGrid
+PASS: Should have 0 grids displayed.
+Requesting to show grid overlay for first .grid-container
+PASS: Should have 1 grid displayed.
+Requesting to show a different grid overlay for first .grid-container
+PASS: Should have 1 grid displayed.
+Requesting to hide grid overlay
+PASS: Should have 0 grids displayed.
+
+-- Running test case: DOM.showGridOverlay.ShowTwoGrids
+PASS: Should have 0 grids displayed.
+Requesting to show first grid overlay
+PASS: Should have 1 grid displayed.
+Requesting to show second grid overlay
+PASS: Should have 2 grids displayed.
+Requesting to hide first grid overlay
+PASS: Should have 1 grid displayed.
+Requesting to hide second grid overlay
+PASS: Should have 0 grids displayed.
+
+-- Running test case: DOM.showGridOverlay.HideAllGrids
+PASS: Should have 0 grids displayed.
+Requesting to show grid overlay
+PASS: Should have 1 grid displayed.
+Requesting to show a different grid overlay
+PASS: Should have 2 grids displayed.
+Requesting to hide all grid overlays. Hiding all grids is idempotent and should not throw an error.
+PASS: Should have 0 grids displayed.
+Requesting to hide all grid overlays again, expecting none to be cleared. Hiding all grids is idempotent and should not throw an error.
+PASS: Should have 0 grids displayed.
+
+-- Running test case: DOM.showGridOverlay.HideBeforeShowShouldError
+PASS: Should have 0 grids displayed.
+Requesting to hide grid overlay for .grid-container
+PASS: Should produce an exception.
+Error: No grid overlay exists for the node, so cannot clear.
+Requesting to hide all grid overlays. Hiding all grids is idempotent and should not throw an error.
+PASS: Should have 0 grids displayed.
+
+-- Running test case: DOM.showGridOverlay.ForNonexistentNodeShouldError
+PASS: Should have 0 grids displayed.
+Requesting to show grid overlay for invalid nodeId -1
+PASS: Should produce an exception.
+Error: Missing node for given nodeId
+PASS: Should have 0 grids displayed.
+

Added: trunk/LayoutTests/inspector/dom/showGridOverlay.html (0 => 272197)


--- trunk/LayoutTests/inspector/dom/showGridOverlay.html	                        (rev 0)
+++ trunk/LayoutTests/inspector/dom/showGridOverlay.html	2021-02-02 09:02:31 UTC (rev 272197)
@@ -0,0 +1,199 @@
+<!doctype html>
+<html>
+<head>
+<script src=""
+<script>
+function test()
+{
+    let suite = InspectorTest.createAsyncSuite("DOM.showGridOverlay");
+
+    async function getGridContainerNode() {
+        let doc = await WI.domManager.requestDocument();
+        let nodeId = await doc.querySelector(".grid-container");
+        return WI.domManager.nodeForId(nodeId);
+    }
+
+    async function getAllGridContainerNodes() {
+        let doc = await WI.domManager.requestDocument();
+        let nodeIds = await doc.querySelectorAll(".grid-container");
+        return nodeIds.map((nodeId) => WI.domManager.nodeForId(nodeId));
+    }
+
+    async function gridOverlayCount() {
+        return InspectorTest.evaluateInPage("window.internals.inspectorGridOverlayCount()");
+    }
+
+    async function checkGridOverlayCount(expected) {
+        let actual = await gridOverlayCount();
+        let message;
+        switch (expected) {
+        case 1:
+            message = "Should have 1 grid displayed.";
+            break;
+        default:
+            message = `Should have ${expected} grids displayed.`;
+            break;
+        }
+
+        InspectorTest.expectEqual(actual, expected, message);
+    }
+
+    suite.addTestCase({
+        name: "DOM.showGridOverlay.ShowOneGrid",
+        description: "No error occurs when requesting to show a grid overlay.",
+        async test() {
+            await checkGridOverlayCount(0);
+            let container = await getGridContainerNode();
+
+            InspectorTest.log("Requesting to show grid overlay for first .grid-container");
+            await DOMAgent.showGridOverlay(container.id, WI.Color.fromString("magenta").toProtocol());
+            await checkGridOverlayCount(1);
+
+            // No error should occur if showing grid overlay for a node that already has one.
+            InspectorTest.log("Requesting to show a different grid overlay for first .grid-container");
+            await DOMAgent.showGridOverlay(container.id, WI.Color.fromString("green").toProtocol());
+            await checkGridOverlayCount(1);
+
+            // No error should occur when hiding the grid overlay.
+            InspectorTest.log("Requesting to hide grid overlay");
+            await DOMAgent.hideGridOverlay(container.id);
+            await checkGridOverlayCount(0);
+        }
+    });
+
+    suite.addTestCase({
+        name: "DOM.showGridOverlay.ShowTwoGrids",
+        description: "No error occurs when requesting to show multiple grid overlays.",
+        async test() {
+            await checkGridOverlayCount(0);
+            let [first, second] = await getAllGridContainerNodes();
+
+            InspectorTest.log("Requesting to show first grid overlay");
+            await DOMAgent.showGridOverlay(first.id, WI.Color.fromString("magenta").toProtocol());
+            await checkGridOverlayCount(1);
+
+            // No error should occur if showing grid overlay for a node that already has one.
+            InspectorTest.log("Requesting to show second grid overlay");
+            await DOMAgent.showGridOverlay(second.id, WI.Color.fromString("green").toProtocol());
+            await checkGridOverlayCount(2);
+
+            // No error should occur when hiding the grid overlay.
+            InspectorTest.log("Requesting to hide first grid overlay");
+            await DOMAgent.hideGridOverlay(first.id);
+            await checkGridOverlayCount(1);
+
+            // No error should occur when hiding the grid overlay.
+            InspectorTest.log("Requesting to hide second grid overlay");
+            await DOMAgent.hideGridOverlay(second.id);
+            await checkGridOverlayCount(0);
+        }
+    });
+
+    suite.addTestCase({
+        name: "DOM.showGridOverlay.HideAllGrids",
+        description: "No error occurs when requesting to show multiple grid overlays.",
+        async test() {
+            await checkGridOverlayCount(0);
+            let [first, second] = await getAllGridContainerNodes();
+
+            InspectorTest.log("Requesting to show grid overlay");
+            await DOMAgent.showGridOverlay(first.id, WI.Color.fromString("magenta").toProtocol());
+            await checkGridOverlayCount(1);
+
+            // No error should occur if showing grid overlay for a node that already has one.
+            InspectorTest.log("Requesting to show a different grid overlay");
+            await DOMAgent.showGridOverlay(second.id, WI.Color.fromString("green").toProtocol());
+            await checkGridOverlayCount(2);
+
+            // No error should occur when hiding the grid overlay.
+            InspectorTest.log("Requesting to hide all grid overlays. Hiding all grids is idempotent and should not throw an error.");
+            await DOMAgent.hideGridOverlay();
+            await checkGridOverlayCount(0);
+
+            // No error should occur when hiding the grid overlay.
+            InspectorTest.log("Requesting to hide all grid overlays again, expecting none to be cleared. Hiding all grids is idempotent and should not throw an error.");
+            await DOMAgent.hideGridOverlay();
+            await checkGridOverlayCount(0);
+        }
+    });
+
+    suite.addTestCase({
+        name: "DOM.showGridOverlay.HideBeforeShowShouldError",
+        description: "Return an error when requesting to hide a grid overlay when none is active for the node.",
+        async test() {
+            let container = await getGridContainerNode();
+
+            await checkGridOverlayCount(0);
+
+            InspectorTest.log("Requesting to hide grid overlay for .grid-container");
+            await InspectorTest.expectException(async () => {
+                await DOMAgent.hideGridOverlay(container.id);
+            });
+
+            InspectorTest.log("Requesting to hide all grid overlays. Hiding all grids is idempotent and should not throw an error.");
+            await DOMAgent.hideGridOverlay();
+            await checkGridOverlayCount(0);
+        }
+    });
+
+    suite.addTestCase({
+        name: "DOM.showGridOverlay.ForNonexistentNodeShouldError",
+        description: "Return an error when requesting to show a grid overlay for a nonexistent node.",
+        async test() {
+            await checkGridOverlayCount(0);
+
+            InspectorTest.log("Requesting to show grid overlay for invalid nodeId -1");
+            await InspectorTest.expectException(async () => {
+                await DOMAgent.showGridOverlay(-1, WI.Color.fromString("magenta").toProtocol());
+            });
+
+        await checkGridOverlayCount(0);
+        }
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body _onload_="runTest()">
+    <style>
+        body {
+            margin: 100px;
+        }
+        .grid-container {
+            display: grid;
+            grid-gap: 10px;
+            grid-template-columns: 100px 100px 100px;
+            background-color: white;
+            color: gray;
+        }
+
+        .grid-container > .box {
+            background-color: gray;
+            color: white;
+            border-radius: 5px;
+            padding: 20px;
+            font-size: 150%;
+        }
+    </style>
+
+    <p>Tests for the DOM.showGridOverlay command.</p>
+    <div class="grid-container" style="color: #366">
+        <div class="box a">A</div>
+        <div class="box b">B</div>
+        <div class="box c">C</div>
+        <div class="box d">D</div>
+        <div class="box e">E</div>
+        <div class="box f">F</div>
+    </div>
+    <br />
+    <div class="grid-container" style="color: #636">
+        <div class="box a">A</div>
+        <div class="box b">B</div>
+        <div class="box c">C</div>
+        <div class="box d">D</div>
+        <div class="box e">E</div>
+        <div class="box f">F</div>
+    </div>
+</body>
+</html>

Modified: trunk/LayoutTests/inspector/model/color-expected.txt (272196 => 272197)


--- trunk/LayoutTests/inspector/model/color-expected.txt	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/LayoutTests/inspector/model/color-expected.txt	2021-02-02 09:02:31 UTC (rev 272197)
@@ -543,3 +543,9 @@
 "color(display-p3 1 0 0)" is outside sRGB.
 "color(display-p3 0.93 0.353 0.353)" is outside sRGB.
 
+-- Running test case: WI.Color.toProtocol
+PASS: Should convert rgba(10,20,30,40) to {"r":10,"g":20,"b":30,"a":1}.
+PASS: Should convert rgb(10 20 30 / 40%) to {"r":10,"g":20,"b":30,"a":0.4}.
+PASS: Should convert #a0Aa0A to {"r":160,"g":170,"b":10,"a":1}.
+PASS: Should convert rgb(10% 20% 30% / 40%) to {"r":25.5,"g":51,"b":76.5,"a":0.4}.
+

Modified: trunk/LayoutTests/inspector/model/color.html (272196 => 272197)


--- trunk/LayoutTests/inspector/model/color.html	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/LayoutTests/inspector/model/color.html	2021-02-02 09:02:31 UTC (rev 272197)
@@ -672,6 +672,25 @@
         }
     });
 
+    suite.addTestCase({
+        name: "WI.Color.toProtocol",
+        description: "Test serialization of WI.Color into DOM.RGBAColor.",
+        test() {
+            function testInput(input, expected) {
+                let color = WI.Color.fromString(input);
+                let actual = color.toProtocol();
+                InspectorTest.expectShallowEqual(actual, expected, `Should convert ${input} to ${JSON.stringify(expected)}.`);
+            }
+
+            testInput("rgba(10,20,30,40)", {r: 10, g: 20, b: 30, a: 1});
+            testInput("rgb(10 20 30 / 40%)", {r: 10, g: 20, b: 30, a: 0.4});
+            testInput("#a0Aa0A", {r: 160, g: 170, b: 10, a: 1});
+            testInput("rgb(10% 20% 30% / 40%)", {r: 25.5, g: 51, b: 76.5, a: 0.4});
+
+            return true;
+        }
+    });
+
     suite.runTestCasesAndFinish();
 }
 </script>

Modified: trunk/LayoutTests/inspector/unit-tests/test-harness-expect-functions-async-expected.txt (272196 => 272197)


--- trunk/LayoutTests/inspector/unit-tests/test-harness-expect-functions-async-expected.txt	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/LayoutTests/inspector/unit-tests/test-harness-expect-functions-async-expected.txt	2021-02-02 09:02:31 UTC (rev 272197)
@@ -17,6 +17,7 @@
 FAIL: Should produce an exception.
     Expected: not null
     Actual: null
+PASS: Exception-producing work should not return a value
 PASS: Rejected value should be the returned value.
 
 -- Running test case: expectException.AsyncWorkThatRejects
@@ -30,6 +31,9 @@
 FAIL: Should produce an exception.
     Expected: not null
     Actual: null
+FAIL: Exception-producing work should not return a value
+    Expected: undefined
+    Actual: 42
 PASS: Rejected value should be the returned value.
 
 -- Running test case: expectException.AsyncWorkThatResolvesImplicitly
@@ -37,5 +41,6 @@
 FAIL: Should produce an exception.
     Expected: not null
     Actual: null
+PASS: Exception-producing work should not return a value
 PASS: Implicitly resolved value should be undefined.
 

Modified: trunk/Source/_javascript_Core/ChangeLog (272196 => 272197)


--- trunk/Source/_javascript_Core/ChangeLog	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/_javascript_Core/ChangeLog	2021-02-02 09:02:31 UTC (rev 272197)
@@ -1,3 +1,17 @@
+2021-02-02  BJ Burg  <[email protected]>
+
+        Web Inspector: implement the basics for showing/hiding grid overlays
+        https://bugs.webkit.org/show_bug.cgi?id=221062
+
+        Reviewed by Devin Rousso.
+
+        Add new commands to show and hide CSS grid overlays:
+
+            - DOM.showGridOverlay
+            - DOM.hideGridOverlay
+
+        * inspector/protocol/DOM.json:
+
 2021-02-02  Adrian Perez de Castro  <[email protected]>
 
         Non-unified build fixes, late January 2021 edition

Modified: trunk/Source/_javascript_Core/inspector/protocol/DOM.json (272196 => 272197)


--- trunk/Source/_javascript_Core/inspector/protocol/DOM.json	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/_javascript_Core/inspector/protocol/DOM.json	2021-02-02 09:02:31 UTC (rev 272197)
@@ -494,6 +494,28 @@
             "description": "Highlights owner element of the frame with given id."
         },
         {
+            "name": "showGridOverlay",
+            "description": "Shows a grid overlay for a node that begins a 'grid' layout context. The command has no effect if <code>nodeId</code> is invalid or the associated node does not begin a 'grid' layout context. A node can only have one grid overlay at a time; subsequent calls with the same <code>nodeId</code> will override earlier calls.",
+            "targetTypes": ["page"],
+            "parameters": [
+                { "name": "nodeId", "$ref": "NodeId", "description": "The node for which a grid overlay should be shown." },
+                { "name": "gridColor", "$ref": "RGBAColor", "description": "The primary color to use for the grid overlay." },
+                { "name": "showLineNames", "type": "boolean", "optional": true, "description": "Show labels for grid line names. If not specified, the default value is false." },
+                { "name": "showLineNumbers", "type": "boolean", "optional": true, "description": "Show labels for grid line numbers. If not specified, the default value is false." },
+                { "name": "showExtendedGridlines", "type": "boolean", "optional": true, "description": "Show grid lines that extend beyond the bounds of the grid. If not specified, the default value is false." },
+                { "name": "showTrackSizes", "type": "boolean", "optional": true, "description": "Show grid track size information. If not specified, the default value is false." },
+                { "name": "showAreaNames", "type": "boolean", "optional": true, "description": "Show labels for grid area names. If not specified, the default value is false." }
+            ]
+        },
+        {
+            "name": "hideGridOverlay",
+            "description": "Hides a grid overlay for a node that begins a 'grid' layout context. The command has no effect if <code>nodeId</code> is specified and invalid, or if there is not currently an overlay set for the <code>nodeId</code>.",
+            "targetTypes": ["page"],
+            "parameters": [
+                { "name": "nodeId", "$ref": "NodeId", "optional": true, "description": "The node for which a grid overlay should be hidden. If a <code>nodeId</code> is not specified, all grid overlays will be hidden." }
+            ]
+        },
+        {
             "name": "pushNodeByPathToFrontend",
             "description": "Requests that the node is sent to the caller given its path.",
             "targetTypes": ["page"],

Modified: trunk/Source/WebCore/ChangeLog (272196 => 272197)


--- trunk/Source/WebCore/ChangeLog	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/WebCore/ChangeLog	2021-02-02 09:02:31 UTC (rev 272197)
@@ -1,3 +1,58 @@
+2021-02-02  BJ Burg  <[email protected]>
+
+        Web Inspector: implement the basics for showing/hiding grid overlays
+        https://bugs.webkit.org/show_bug.cgi?id=221062
+
+        Reviewed by Devin Rousso.
+
+        Implement backend commands for showing and hiding CSS grid overlays.
+        This patch draws a very simplistic grid overlay. Support for the
+        various grid overlay options will be added in later patches.
+
+        New test: inspector/dom/showGridOverlay.html
+
+        * inspector/InspectorOverlay.h:
+        (WebCore::InspectorOverlay::gridOverlayCount const):
+        Added, for testing only.
+
+        * inspector/InspectorOverlay.cpp:
+        (WebCore::InspectorOverlay::paint):
+        (WebCore::InspectorOverlay::shouldShowOverlay const):
+        Hook up the painting of any active grid overlays.
+
+        (WebCore::InspectorOverlay::setGridOverlayForNode):
+        (WebCore::InspectorOverlay::clearGridOverlayForNode):
+        (WebCore::InspectorOverlay::clearAllGridOverlays):
+        Maintain the list of active grid overlays. A node can only
+        have one grid overlay at a time.
+
+        (WebCore::InspectorOverlay::drawNodeHighlight):
+        Add a note about why grid overlays are not considered when
+        calculating the ruler exclusion area.
+
+        (WebCore::InspectorOverlay::drawGridOverlay): Added.
+        This drawing code exists to flesh out the rest of this patch,
+        and is obviously incomplete.
+
+        Draw grid lines that extend to the edge of the viewport,
+        equivalent to `config.showExtendedGridLines = true`.
+
+        * inspector/agents/InspectorDOMAgent.h:
+        * inspector/agents/InspectorDOMAgent.cpp:
+        (WebCore::InspectorDOMAgent::showGridOverlay):
+        (WebCore::InspectorDOMAgent::hideGridOverlay):
+        Translate protocol commands into InspectorOverlay method calls.
+
+        * inspector/InspectorController.h:
+        * inspector/InspectorController.cpp:
+        (WebCore::InspectorController::gridOverlayCount const):
+        Added. This is used for testing purposes only.
+
+        * testing/Internals.h:
+        * testing/Internals.idl:
+        * testing/Internals.cpp:
+        (WebCore::Internals::inspectorGridOverlayCount): Added.
+
 2021-02-02  Carlos Garcia Campos  <[email protected]>
 
         [SOUP] Stop using SoupBuffer in preparation for libsoup3

Modified: trunk/Source/WebCore/inspector/InspectorController.cpp (272196 => 272197)


--- trunk/Source/WebCore/inspector/InspectorController.cpp	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/WebCore/inspector/InspectorController.cpp	2021-02-02 09:02:31 UTC (rev 272197)
@@ -361,6 +361,11 @@
     m_overlay->getHighlight(highlight, coordinateSystem);
 }
 
+unsigned InspectorController::gridOverlayCount() const
+{
+    return m_overlay->gridOverlayCount();
+}
+
 bool InspectorController::shouldShowOverlay() const
 {
     return m_overlay->shouldShowOverlay();

Modified: trunk/Source/WebCore/inspector/InspectorController.h (272196 => 272197)


--- trunk/Source/WebCore/inspector/InspectorController.h	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/WebCore/inspector/InspectorController.h	2021-02-02 09:02:31 UTC (rev 272197)
@@ -101,9 +101,11 @@
     WEBCORE_EXPORT void willComposite(Frame&);
     WEBCORE_EXPORT void didComposite(Frame&);
 
+    // Testing support.
     bool isUnderTest() const { return m_isUnderTest; }
     void setIsUnderTest(bool isUnderTest) { m_isUnderTest = isUnderTest; }
     WEBCORE_EXPORT void evaluateForTestInFrontend(const String& script);
+    WEBCORE_EXPORT unsigned gridOverlayCount() const;
 
     InspectorClient* inspectorClient() const { return m_inspectorClient; }
     InspectorFrontendClient* inspectorFrontendClient() const { return m_inspectorFrontendClient; }

Modified: trunk/Source/WebCore/inspector/InspectorOverlay.cpp (272196 => 272197)


--- trunk/Source/WebCore/inspector/InspectorOverlay.cpp	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/WebCore/inspector/InspectorOverlay.cpp	2021-02-02 09:02:31 UTC (rev 272197)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2011 Google Inc. All rights reserved.
- * Copyright (C) 2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2019-2021 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -43,6 +43,7 @@
 #include "Frame.h"
 #include "FrameView.h"
 #include "GraphicsContext.h"
+#include "GridPositionsResolver.h"
 #include "InspectorClient.h"
 #include "IntPoint.h"
 #include "IntRect.h"
@@ -53,6 +54,7 @@
 #include "PseudoElement.h"
 #include "RenderBox.h"
 #include "RenderBoxModelObject.h"
+#include "RenderGrid.h"
 #include "RenderInline.h"
 #include "RenderObject.h"
 #include "Settings.h"
@@ -409,6 +411,9 @@
         rulerExclusion.titlePath = nodeRulerExclusion.titlePath;
     }
 
+    for (const InspectorOverlay::Grid& gridOverlay : m_activeGridOverlays)
+        drawGridOverlay(context, gridOverlay);
+
     if (!m_paintRects.isEmpty())
         drawPaintRects(context, m_paintRects);
 
@@ -495,7 +500,7 @@
 {
     // Don't show the overlay when m_showRulersDuringElementSelection is true, as it's only supposed
     // to have an effect when element selection is active (e.g. a node is hovered).
-    return m_highlightNode || m_highlightNodeList || m_highlightQuad || m_indicating || m_showPaintRects || m_showRulers;
+    return m_highlightNode || m_highlightNodeList || m_highlightQuad || m_indicating || m_showPaintRects || m_showRulers || m_activeGridOverlays.size();
 }
 
 void InspectorOverlay::update()
@@ -555,6 +560,46 @@
     update();
 }
 
+bool InspectorOverlay::removeGridOverlayForNode(Node& node)
+{
+    // Try to remove `node`. Also clear any grid overlays whose WeakPtr<Node> has been cleared.
+    return m_activeGridOverlays.removeAllMatching([&] (const InspectorOverlay::Grid& gridOverlay) {
+        return !gridOverlay.gridNode || gridOverlay.gridNode.get() == &node;
+    });
+}
+
+ErrorStringOr<void> InspectorOverlay::setGridOverlayForNode(Node& node, const InspectorOverlay::Grid::Config& gridOverlayConfig)
+{
+    RenderObject* renderer = node.renderer();
+    if (!is<RenderGrid>(renderer))
+        return makeUnexpected("Node does not initiate a grid context");
+
+    removeGridOverlayForNode(node);
+
+    m_activeGridOverlays.append({ makeWeakPtr(node), gridOverlayConfig });
+
+    update();
+
+    return { };
+}
+
+ErrorStringOr<void> InspectorOverlay::clearGridOverlayForNode(Node& node)
+{
+    if (!removeGridOverlayForNode(node))
+        return makeUnexpected("No grid overlay exists for the node, so cannot clear.");
+
+    update();
+
+    return { };
+}
+
+void InspectorOverlay::clearAllGridOverlays()
+{
+    m_activeGridOverlays.clear();
+
+    update();
+}
+
 void InspectorOverlay::updatePaintRectsTimerFired()
 {
     MonotonicTime now = MonotonicTime::now();
@@ -587,6 +632,9 @@
     if (m_nodeHighlightConfig.showInfo)
         rulerExclusion.titlePath = drawElementTitle(context, node, rulerExclusion.bounds);
 
+    // Note: since grid overlays may cover the entire viewport with little lines, grid overlay bounds
+    // are not considered as part of the combined bounds used as the ruler exclusion area.
+    
     return rulerExclusion;
 }
 
@@ -1092,4 +1140,82 @@
     return path;
 }
 
+void InspectorOverlay::drawGridOverlay(GraphicsContext& context, const InspectorOverlay::Grid& gridOverlay)
+{
+    // If the node WeakPtr has been cleared, then the node is gone and there's nothing to draw.
+    if (!gridOverlay.gridNode) {
+        m_activeGridOverlays.removeAllMatching([&] (const InspectorOverlay::Grid& gridOverlay) {
+            return !gridOverlay.gridNode;
+        });
+        return;
+    }
+    
+    // Always re-check because the node's renderer may have changed since being added.
+    // If renderer is no longer a grid, then remove the grid overlay for the node.
+    Node* node = gridOverlay.gridNode.get();
+    auto renderer = node->renderer();
+    if (!is<RenderGrid>(renderer)) {
+        removeGridOverlayForNode(*node);
+        return;
+    }
+
+    auto* renderGrid = downcast<RenderGrid>(renderer);
+    LayoutRect paddingBox = renderGrid->clientBoxRect();
+    LayoutRect contentBox = LayoutRect(paddingBox.x() + renderGrid->paddingLeft(), paddingBox.y() + renderGrid->paddingTop(),
+        paddingBox.width() - renderGrid->paddingLeft() - renderGrid->paddingRight(), paddingBox.height() - renderGrid->paddingTop() - renderGrid->paddingBottom());
+    FloatQuad absContentQuad = renderer->localToAbsoluteQuad(FloatRect(contentBox));
+    FloatRect gridBoundingBox = absContentQuad.boundingBox();
+    FrameView* pageView = m_page.mainFrame().view();
+    FloatSize contentInset(0, pageView->topContentInset(ScrollView::TopContentInsetType::WebCoreOrPlatformContentInset));
+    FloatSize viewportSize = pageView->sizeForVisibleContent();
+
+    GraphicsContextStateSaver saver(context);
+
+    // Drawing code is relative to the visible viewport area.
+    context.translate(0, contentInset.height());
+    
+    // FIXME: if showExtendedGridlines is false, set the clip path to the gridBoundingBox (maybe inflated?)
+
+    // Draw columns and rows.
+    context.setStrokeThickness(1);
+    context.setStrokeColor(gridOverlay.config.gridColor);
+
+    auto columnPositions = renderGrid->columnPositions();
+    auto columnWidths = renderGrid->trackSizesForComputedStyle(GridTrackSizingDirection::ForColumns);
+    for (unsigned i = 0; i < columnPositions.size(); ++i) {
+        // Column positions are (apparently) relative to the element's content area.
+        auto position = columnPositions[i] + gridBoundingBox.x();
+
+        Path columnPaths;
+        columnPaths.moveTo({ position, 0 });
+        columnPaths.addLineTo({ position, viewportSize.height() });
+
+        if (i < columnWidths.size()) {
+            auto width = columnWidths[i];
+            columnPaths.moveTo({ position + width, 0 });
+            columnPaths.addLineTo({ position + width, viewportSize.height() });
+        }
+
+        context.strokePath(columnPaths);
+    }
+
+    auto rowPositions = renderGrid->rowPositions();
+    auto rowHeights = renderGrid->trackSizesForComputedStyle(GridTrackSizingDirection::ForRows);
+    for (unsigned i = 0; i < rowPositions.size(); ++i) {
+        auto position = rowPositions[i] + gridBoundingBox.y();
+
+        Path rowPaths;
+        rowPaths.moveTo({ 0, position });
+        rowPaths.addLineTo({ viewportSize.width(), position });
+
+        if (i < rowHeights.size()) {
+            auto height = rowHeights[i];
+            rowPaths.moveTo({ 0, position + height });
+            rowPaths.addLineTo({ viewportSize.width(), position + height });
+        }
+
+        context.strokePath(rowPaths);
+    }
+}
+
 } // namespace WebCore

Modified: trunk/Source/WebCore/inspector/InspectorOverlay.h (272196 => 272197)


--- trunk/Source/WebCore/inspector/InspectorOverlay.h	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/WebCore/inspector/InspectorOverlay.h	2021-02-02 09:02:31 UTC (rev 272197)
@@ -39,8 +39,16 @@
 #include <wtf/Optional.h>
 #include <wtf/RefPtr.h>
 #include <wtf/Vector.h>
+#include <wtf/WeakPtr.h>
 #include <wtf/text/WTFString.h>
 
+namespace Inspector {
+using ErrorString = String;
+
+template <typename T>
+using ErrorStringOr = Expected<T, ErrorString>;
+}
+
 namespace WebCore {
 
 class GraphicsContext;
@@ -98,6 +106,24 @@
         using Bounds = FloatRect;
     };
 
+    struct Grid {
+        WTF_MAKE_STRUCT_FAST_ALLOCATED;
+
+        struct Config {
+            WTF_MAKE_STRUCT_FAST_ALLOCATED;
+
+            Color gridColor;
+            bool showLineNames;
+            bool showLineNumbers;
+            bool showExtendedGridlines;
+            bool showTrackSizes;
+            bool showAreaNames;
+        };
+
+        WeakPtr<Node> gridNode;
+        Config config;
+    };
+
     enum class CoordinateSystem {
         View, // Adjusts for the main frame's scroll offset.
         Document, // Does not adjust for the main frame's scroll offset.
@@ -120,11 +146,18 @@
     void setShowRulersDuringElementSelection(bool enabled) { m_showRulersDuringElementSelection = enabled; }
 
     Node* highlightedNode() const;
+    unsigned gridOverlayCount() const { return m_activeGridOverlays.size(); }
 
     void didSetSearchingForNode(bool enabled);
 
     void setIndicating(bool indicating);
 
+    // Multiple grid overlays can be active at the same time. These methods
+    // will fail if the node is not a grid or if the node has been GC'd.
+    Inspector::ErrorStringOr<void> setGridOverlayForNode(Node&, const InspectorOverlay::Grid::Config&);
+    Inspector::ErrorStringOr<void> clearGridOverlayForNode(Node&);
+    void clearAllGridOverlays();
+
 private:
     using TimeRectPair = std::pair<MonotonicTime, FloatRect>;
 
@@ -141,8 +174,12 @@
 
     Path drawElementTitle(GraphicsContext&, Node&, const Highlight::Bounds&);
 
+    void drawGridOverlay(GraphicsContext&, const InspectorOverlay::Grid&);
+
     void updatePaintRectsTimerFired();
 
+    bool removeGridOverlayForNode(Node&);
+
     Page& m_page;
     InspectorClient* m_client;
 
@@ -156,6 +193,8 @@
     Deque<TimeRectPair> m_paintRects;
     Timer m_paintRectUpdateTimer;
 
+    Vector<InspectorOverlay::Grid> m_activeGridOverlays;
+
     bool m_indicating { false };
     bool m_showPaintRects { false };
     bool m_showRulers { false };

Modified: trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp (272196 => 272197)


--- trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp	2021-02-02 09:02:31 UTC (rev 272197)
@@ -116,6 +116,7 @@
 #include <_javascript_Core/JSCInlines.h>
 #include <pal/crypto/CryptoDigest.h>
 #include <wtf/Function.h>
+#include <wtf/Optional.h>
 #include <wtf/text/Base64.h>
 #include <wtf/text/CString.h>
 #include <wtf/text/WTFString.h>
@@ -129,26 +130,26 @@
 static const size_t maxTextSize = 10000;
 static const UChar ellipsisUChar[] = { 0x2026, 0 };
 
-static Color parseColor(RefPtr<JSON::Object>&& colorObject)
+static Optional<Color> parseColor(RefPtr<JSON::Object>&& colorObject)
 {
     if (!colorObject)
-        return Color::transparentBlack;
+        return WTF::nullopt;
 
     auto r = colorObject->getInteger(Protocol::DOM::RGBAColor::rKey);
     auto g = colorObject->getInteger(Protocol::DOM::RGBAColor::gKey);
     auto b = colorObject->getInteger(Protocol::DOM::RGBAColor::bKey);
     if (!r || !g || !b)
-        return Color::transparentBlack;
+        return WTF::nullopt;
 
     auto a = colorObject->getDouble(Protocol::DOM::RGBAColor::aKey);
     if (!a)
-        return makeFromComponentsClamping<SRGBA<uint8_t>>(*r, *g, *b);
-    return makeFromComponentsClampingExceptAlpha<SRGBA<uint8_t>>(*r, *g, *b, convertFloatAlphaTo<uint8_t>(*a));
+        return { makeFromComponentsClamping<SRGBA<uint8_t>>(*r, *g, *b) };
+    return { makeFromComponentsClampingExceptAlpha<SRGBA<uint8_t>>(*r, *g, *b, convertFloatAlphaTo<uint8_t>(*a)) };
 }
 
 static Color parseConfigColor(const String& fieldName, JSON::Object& configObject)
 {
-    return parseColor(configObject.getObject(fieldName));
+    return parseColor(configObject.getObject(fieldName)).valueOr(Color::transparentBlack);
 }
 
 static bool parseQuad(Ref<JSON::Array>&& quadArray, FloatQuad* quad)
@@ -1291,8 +1292,8 @@
 void InspectorDOMAgent::innerHighlightQuad(std::unique_ptr<FloatQuad> quad, RefPtr<JSON::Object>&& color, RefPtr<JSON::Object>&& outlineColor, Optional<bool>&& usePageCoordinates)
 {
     auto highlightConfig = makeUnique<InspectorOverlay::Highlight::Config>();
-    highlightConfig->content = parseColor(WTFMove(color));
-    highlightConfig->contentOutline = parseColor(WTFMove(outlineColor));
+    highlightConfig->content = parseColor(WTFMove(color)).valueOr(Color::transparentBlack);
+    highlightConfig->contentOutline = parseColor(WTFMove(outlineColor)).valueOr(Color::transparentBlack);
     highlightConfig->usePageCoordinates = usePageCoordinates ? *usePageCoordinates : false;
     m_overlay->highlightQuad(WTFMove(quad), *highlightConfig);
 }
@@ -1458,8 +1459,8 @@
     if (frame->ownerElement()) {
         auto highlightConfig = makeUnique<InspectorOverlay::Highlight::Config>();
         highlightConfig->showInfo = true; // Always show tooltips for frames.
-        highlightConfig->content = parseColor(WTFMove(color));
-        highlightConfig->contentOutline = parseColor(WTFMove(outlineColor));
+        highlightConfig->content = parseColor(WTFMove(color)).valueOr(Color::transparentBlack);
+        highlightConfig->contentOutline = parseColor(WTFMove(outlineColor)).valueOr(Color::transparentBlack);
         m_overlay->highlightNode(frame->ownerElement(), *highlightConfig);
     }
 
@@ -1473,6 +1474,46 @@
     return { };
 }
 
+Inspector::Protocol::ErrorStringOr<void> InspectorDOMAgent::showGridOverlay(Inspector::Protocol::DOM::NodeId nodeId,  Ref<JSON::Object>&& gridColor, Optional<bool>&& showLineNames, Optional<bool>&& showLineNumbers, Optional<bool>&& showExtendedGridlines, Optional<bool>&& showTrackSizes, Optional<bool>&& showAreaNames)
+{
+    Protocol::ErrorString errorString;
+    Node* node = assertNode(errorString, nodeId);
+    if (!node)
+        return makeUnexpected(errorString);
+
+    auto parsedColor = parseColor(WTFMove(gridColor));
+    if (!parsedColor)
+        return makeUnexpected("Invalid color could not be parsed.");
+
+    InspectorOverlay::Grid::Config config;
+    config.gridColor = *parsedColor;
+    config.showLineNames = showLineNames.valueOr(false);
+    config.showLineNumbers = showLineNumbers.valueOr(false);
+    config.showExtendedGridlines = showExtendedGridlines.valueOr(false);
+    config.showTrackSizes = showTrackSizes.valueOr(false);
+    config.showAreaNames = showAreaNames.valueOr(false);
+
+    m_overlay->setGridOverlayForNode(*node, config);
+
+    return { };
+}
+
+Inspector::Protocol::ErrorStringOr<void> InspectorDOMAgent::hideGridOverlay(Optional<Protocol::DOM::NodeId>&& nodeId)
+{
+    if (nodeId) {
+        Protocol::ErrorString errorString;
+        auto node = assertNode(errorString, *nodeId);
+        if (!node)
+            return makeUnexpected(errorString);
+
+        return m_overlay->clearGridOverlayForNode(*node);
+}
+
+    m_overlay->clearAllGridOverlays();
+
+    return { };
+}
+
 Protocol::ErrorStringOr<Protocol::DOM::NodeId> InspectorDOMAgent::moveTo(Protocol::DOM::NodeId nodeId, Protocol::DOM::NodeId targetNodeId, Optional<Protocol::DOM::NodeId>&& insertBeforeNodeId)
 {
     Protocol::ErrorString errorString;

Modified: trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.h (272196 => 272197)


--- trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.h	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.h	2021-02-02 09:02:31 UTC (rev 272197)
@@ -144,6 +144,8 @@
     Inspector::Protocol::ErrorStringOr<void> highlightNode(Ref<JSON::Object>&& highlightConfig, Optional<Inspector::Protocol::DOM::NodeId>&&, const Inspector::Protocol::Runtime::RemoteObjectId&);
     Inspector::Protocol::ErrorStringOr<void> highlightNodeList(Ref<JSON::Array>&& nodeIds, Ref<JSON::Object>&& highlightConfig);
     Inspector::Protocol::ErrorStringOr<void> highlightFrame(const Inspector::Protocol::Network::FrameId&, RefPtr<JSON::Object>&& color, RefPtr<JSON::Object>&& outlineColor);
+    Inspector::Protocol::ErrorStringOr<void> showGridOverlay(Inspector::Protocol::DOM::NodeId, Ref<JSON::Object>&& gridColor, Optional<bool>&& showLineNames, Optional<bool>&& showLineNumbers, Optional<bool>&& showExtendedGridlines, Optional<bool>&& showTrackSizes, Optional<bool>&& showAreaNames);
+    Inspector::Protocol::ErrorStringOr<void> hideGridOverlay(Optional<Inspector::Protocol::DOM::NodeId>&&);
     Inspector::Protocol::ErrorStringOr<Inspector::Protocol::DOM::NodeId> moveTo(Inspector::Protocol::DOM::NodeId nodeId, Inspector::Protocol::DOM::NodeId targetNodeId, Optional<Inspector::Protocol::DOM::NodeId>&& insertBeforeNodeId);
     Inspector::Protocol::ErrorStringOr<void> undo();
     Inspector::Protocol::ErrorStringOr<void> redo();
@@ -213,6 +215,7 @@
     void highlightMousedOverNode();
     void setSearchingForNode(Inspector::Protocol::ErrorString&, bool enabled, RefPtr<JSON::Object>&& highlightConfig, bool showRulers);
     std::unique_ptr<InspectorOverlay::Highlight::Config> highlightConfigFromInspectorObject(Inspector::Protocol::ErrorString&, RefPtr<JSON::Object>&& highlightInspectorObject);
+    std::unique_ptr<InspectorOverlay::Grid::Config> gridOverlayConfigFromInspectorObject(Inspector::Protocol::ErrorString&, RefPtr<JSON::Object>&& gridOverlayInspectorObject);
 
     // Node-related methods.
     typedef HashMap<RefPtr<Node>, Inspector::Protocol::DOM::NodeId> NodeToIdMap;

Modified: trunk/Source/WebCore/testing/Internals.cpp (272196 => 272197)


--- trunk/Source/WebCore/testing/Internals.cpp	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/WebCore/testing/Internals.cpp	2021-02-02 09:02:31 UTC (rev 272197)
@@ -1664,6 +1664,15 @@
     return DOMRect::create(renderer->absoluteBoundingBoxRectIgnoringTransforms());
 }
 
+ExceptionOr<unsigned> Internals::inspectorGridOverlayCount()
+{
+    Document* document = contextDocument();
+    if (!document || !document->page())
+        return Exception { InvalidAccessError };
+
+    return document->page()->inspectorController().gridOverlayCount();
+}
+
 ExceptionOr<Ref<DOMRectList>> Internals::inspectorHighlightRects()
 {
     Document* document = contextDocument();

Modified: trunk/Source/WebCore/testing/Internals.h (272196 => 272197)


--- trunk/Source/WebCore/testing/Internals.h	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/WebCore/testing/Internals.h	2021-02-02 09:02:31 UTC (rev 272197)
@@ -271,6 +271,7 @@
     Ref<DOMRect> boundingBox(Element&);
 
     ExceptionOr<Ref<DOMRectList>> inspectorHighlightRects();
+    ExceptionOr<unsigned> inspectorGridOverlayCount();
 
     ExceptionOr<unsigned> markerCountForNode(Node&, const String&);
     ExceptionOr<RefPtr<Range>> markerRangeForNode(Node&, const String& markerType, unsigned index);

Modified: trunk/Source/WebCore/testing/Internals.idl (272196 => 272197)


--- trunk/Source/WebCore/testing/Internals.idl	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/WebCore/testing/Internals.idl	2021-02-02 09:02:31 UTC (rev 272197)
@@ -329,6 +329,7 @@
 
     DOMRect boundingBox(Element element);
 
+    [MayThrowException] unsigned long inspectorGridOverlayCount();
     [MayThrowException] DOMRectList inspectorHighlightRects();
 
     [MayThrowException] unsigned long markerCountForNode(Node node, DOMString markerType);

Modified: trunk/Source/WebInspectorUI/ChangeLog (272196 => 272197)


--- trunk/Source/WebInspectorUI/ChangeLog	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/WebInspectorUI/ChangeLog	2021-02-02 09:02:31 UTC (rev 272197)
@@ -1,3 +1,54 @@
+2021-02-02  BJ Burg  <[email protected]>
+
+        Web Inspector: implement the basics for showing/hiding grid overlays
+        https://bugs.webkit.org/show_bug.cgi?id=221062
+
+        Reviewed by Devin Rousso.
+
+        Expose new DOM.showGridOverlay and DOM.hideGridOverlay commands
+        via WI.DOMNode. Add initial engineering UI to toggle grid overlays.
+
+        New methods are covered by a new test:
+
+            inspector/dom/showGridOverlay.html
+
+        Additions to WI.DOMManager.prototype.requestDocument are covered
+        by existing tests (callback case) and a new test (promise case).
+
+        Additions to WI.Color are covered by a new test case in:
+
+            inspector/model/color.html
+
+        * UserInterface/Controllers/DOMManager.js:
+        (WI.DOMManager.prototype.requestDocument):
+        (WI.DOMManager.prototype._requestDocumentWithPromise):
+        Drive-by: upgrade requestDocument() to return a promise if
+        no callback argument was passed. This is used by a new test.
+
+        * UserInterface/Models/Color.js:
+        (WI.Color.prototype.toProtocol): Added. The protocol object is
+        DOM.RGBAColor, which is the same thing as WI.Color.Format.RGBA.
+
+        * UserInterface/Models/DOMNode.js:
+        (WI.DOMNode.prototype.showGridOverlay):
+        (WI.DOMNode.prototype.hideGridOverlay):
+        Added. These are the methods that the rest of WebInspectorUI uses
+        to interact with grid overlays for a particular node. Note that
+        these methods return either an unsettled promise (from the agent call)
+        or a rejected promise (in the case that the node is destroyed).
+        This allows test cases to `await` before checking the grid overlay count.
+
+        * UserInterface/Test/TestHarness.js:
+        (TestHarness.prototype.expectException): Improve logging output
+        when InspectorTest.expectException does not catch an exception.
+
+        * UserInterface/Views/ContextMenuUtilities.js:
+        Add some engineering-only context menu items for showing/hiding
+        grid overlays to DOMTreeElements in the Elements Tab.
+
+        These are in place for development purposes and should eventually
+        be removed when no longer needed.
+
 2021-02-01  Patrick Angle  <[email protected]>
 
         REGRESSION(r270637): Web Inspector: Filtering field no longer shows in Computed panel

Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js (272196 => 272197)


--- trunk/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js	2021-02-02 09:02:31 UTC (rev 272197)
@@ -40,6 +40,7 @@
 
         this._idToDOMNode = {};
         this._document = null;
+        this._documentPromise = null;
         this._attributeLoadNodeIds = {};
         this._restoreSelectedNodeIsAllowed = true;
         this._loadNodeAttributesTimeout = 0;
@@ -155,36 +156,10 @@
 
     requestDocument(callback)
     {
-        if (this._document) {
-            callback(this._document);
-            return;
-        }
+        if (typeof callback !== "function")
+            return this._requestDocumentWithPromise();
 
-        if (this._pendingDocumentRequestCallbacks)
-            this._pendingDocumentRequestCallbacks.push(callback);
-        else
-            this._pendingDocumentRequestCallbacks = [callback];
-
-        if (this._hasRequestedDocument)
-            return;
-
-        if (!WI.pageTarget)
-            return;
-
-        if (!WI.pageTarget.hasDomain("DOM"))
-            return;
-
-        this._hasRequestedDocument = true;
-
-        WI.pageTarget.DOMAgent.getDocument((error, root) => {
-            if (!error)
-                this._setDocument(root);
-
-            for (let callback of this._pendingDocumentRequestCallbacks)
-                callback(this._document);
-
-            this._pendingDocumentRequestCallbacks = null;
-        });
+        this._requestDocumentWithCallback(callback);
     }
 
     ensureDocument()
@@ -264,6 +239,57 @@
         this.requestDocument(onDocumentAvailable.bind(this));
     }
 
+    _requestDocumentWithPromise()
+    {
+        if (this._documentPromise)
+            return this._documentPromise.promise;
+
+        this._documentPromise = new WI.WrappedPromise;
+        if (this._document)
+            this._documentPromise.resolve(this._document);
+        else {
+            this._requestDocumentWithCallback((doc) => {
+                this._documentPromise.resolve(doc);
+            });
+        }
+
+        return this._documentPromise.promise;
+    }
+
+    _requestDocumentWithCallback(callback)
+    {
+        if (this._document) {
+            callback(this._document);
+            return;
+        }
+
+        if (this._pendingDocumentRequestCallbacks)
+            this._pendingDocumentRequestCallbacks.push(callback);
+        else
+            this._pendingDocumentRequestCallbacks = [callback];
+
+        if (this._hasRequestedDocument)
+            return;
+
+        if (!WI.pageTarget)
+            return;
+
+        if (!WI.pageTarget.hasDomain("DOM"))
+            return;
+
+        this._hasRequestedDocument = true;
+
+        WI.pageTarget.DOMAgent.getDocument((error, root) => {
+            if (!error)
+                this._setDocument(root);
+
+            for (let callback of this._pendingDocumentRequestCallbacks)
+                callback(this._document);
+
+            this._pendingDocumentRequestCallbacks = null;
+        });
+    }
+
     _attributeModified(nodeId, name, value)
     {
         var node = this._idToDOMNode[nodeId];
@@ -361,6 +387,9 @@
 
         this._document = newDocument;
 
+        // Force the promise to be recreated so that it resolves to the new document.
+        this._documentPromise = null;
+
         if (!this._document)
             this._hasRequestedDocument = false;
 

Modified: trunk/Source/WebInspectorUI/UserInterface/Models/Color.js (272196 => 272197)


--- trunk/Source/WebInspectorUI/UserInterface/Models/Color.js	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/Color.js	2021-02-02 09:02:31 UTC (rev 272197)
@@ -638,6 +638,12 @@
         return "";
     }
 
+    toProtocol()
+    {
+        let [r, g, b, a] = this.rgba;
+        return {r, g, b, a};
+    }
+
     isKeyword()
     {
         if (this.keyword)

Modified: trunk/Source/WebInspectorUI/UserInterface/Models/DOMNode.js (272196 => 272197)


--- trunk/Source/WebInspectorUI/UserInterface/Models/DOMNode.js	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/DOMNode.js	2021-02-02 09:02:31 UTC (rev 272197)
@@ -553,6 +553,34 @@
         target.DOMAgent.highlightNode(WI.DOMManager.buildHighlightConfig(mode), this.id);
     }
 
+    showGridOverlay(color, {showLineNames, showLineNumbers, showExtendedGridLines, showTrackSizes, showAreaNames} = {})
+    {
+        console.assert(color instanceof WI.Color, color);
+
+        if (this._destroyed)
+            return Promise.reject("Cannot show overlay, node is destroyed");
+
+        let target = WI.assumingMainTarget();
+        return target.DOMAgent.showGridOverlay.invoke({
+            nodeId: this.id,
+            gridColor: color.toProtocol(),
+            showLineNames: !!showLineNames,
+            showLineNumbers: !!showLineNumbers,
+            showExtendedGridLines: !!showExtendedGridLines,
+            showTrackSizes: !!showTrackSizes,
+            showAreaNames: !!showAreaNames,
+        });
+    }
+
+    hideGridOverlay()
+    {
+        if (this._destroyed)
+            return Promise.reject("Cannot hide overlay, node is destroyed");
+
+        let target = WI.assumingMainTarget();
+        return target.DOMAgent.hideGridOverlay(this.id);
+    }
+
     scrollIntoView()
     {
         WI.RemoteObject.resolveNode(this).then((object) => {

Modified: trunk/Source/WebInspectorUI/UserInterface/Test/TestHarness.js (272196 => 272197)


--- trunk/Source/WebInspectorUI/UserInterface/Test/TestHarness.js	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/WebInspectorUI/UserInterface/Test/TestHarness.js	2021-02-02 09:02:31 UTC (rev 272197)
@@ -257,10 +257,12 @@
         if (typeof work !== "function")
             throw new Error("Invalid argument to catchException: work must be a function.");
 
-        let expectAndDumpError = (e) => {
+        let expectAndDumpError = (e, resolvedValue) => {
             this.expectNotNull(e, "Should produce an exception.");
-            if (!e)
+            if (!e) {
+                this.expectEqual(resolvedValue, undefined, "Exception-producing work should not return a value");
                 return;
+            }
 
             if (e instanceof Error || !(e instanceof Object))
                 this.log(e.toString());
@@ -284,7 +286,7 @@
             // Invert the promise's settled state to match the expectation of the caller.
             if (result instanceof Promise) {
                 return result.then((resolvedValue) => {
-                    expectAndDumpError(null);
+                    expectAndDumpError(null, resolvedValue);
                     return Promise.reject(resolvedValue);
                 }, (e) => { // Don't chain the .catch as it will log the value we just rejected.
                     expectAndDumpError(e);

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js (272196 => 272197)


--- trunk/Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js	2021-02-02 08:52:42 UTC (rev 272196)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js	2021-02-02 09:02:31 UTC (rev 272197)
@@ -386,6 +386,28 @@
         }
 
         contextMenu.appendSeparator();
+
+        // FIXME: <https://webkit.org/b/221246> remove these engineering-only menu items when removing the feature flag.
+        if (WI.isEngineeringBuild && WI.settings.experimentalEnableLayoutPanel.value) {
+            if (InspectorBackend.hasCommand("DOM.showGridOverlay") && attached) {
+                contextMenu.appendItem(WI.unlocalizedString("Add Grid Overlay with Random Color"), () => {
+                    let randomComponent = () => Math.floor(Math.random() * 255);
+                    let color = new WI.Color(WI.Color.Format.RGB, [randomComponent(), randomComponent(), randomComponent()]);
+                    domNode.showGridOverlay(color).catch(console.error);
+                });
+
+                contextMenu.appendItem(WI.unlocalizedString("Remove Grid Overlay for this Node"), () => {
+                    domNode.hideGridOverlay();
+                });
+
+                contextMenu.appendItem(WI.unlocalizedString("Remove All Grid Overlays"), () => {
+                    let target = WI.assumingMainTarget();
+                    target.DOMAgent.hideGridOverlay();
+                });
+            }
+        }
+
+        contextMenu.appendSeparator();
     }
 };
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to