Title: [286876] trunk
Revision
286876
Author
rcali...@apple.com
Date
2021-12-10 14:15:52 -0800 (Fri, 10 Dec 2021)

Log Message

Web Inspector: Computed Panel: Group CSS variables by value type
https://bugs.webkit.org/show_bug.cgi?id=233563
<rdar://82978905>

Reviewed by Devin Rousso and Patrick Angle.

Source/WebInspectorUI:

Add the ability to view CSS variables in the Computed styles details sidebar panel
groupped by value type in a few sections: "colors", "dimensions", "numbers" and a
catch-all group of "other".

* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Models/DOMNodeStyles.js:
(WI.DOMNodeStyles):
(WI.DOMNodeStyles.prototype.variableStylesByType):
Iterate on-demand over all CSS variables found in the node's computed styles
and assign each to a group depending on its value type:
- color
- dimension (number followed by a CSS unit-like string)
- number
- other

Additional groups and refinements will come in follow-up patches.

(WI.DOMNodeStyles.prototype.refresh.fetchedComputedStyle):
The map of CSS variable groups gets invalidated when there's a significant
change in the node's computed style. This supports the use case where previously
empty groups become populated or, conversely, become empty.

* UserInterface/Views/ComputedStyleDetailsPanel.js:
(WI.ComputedStyleDetailsPanel):
(WI.ComputedStyleDetailsPanel.prototype.refresh):
(WI.ComputedStyleDetailsPanel.prototype.applyFilter):
(WI.ComputedStyleDetailsPanel.prototype.initialLayout):
No longer generate the elements for laying out CSS variables during `initialLayout()`
but handle them during `layout()`. This support mutating the DOM for laying out
either one top-level list of CSS variables (ungrouped) or multiple lists of CSS variable
groups depeding on the grouping mode selected at runtime.

* UserInterface/Views/ComputedStyleDetailsPanel.css:
(.sidebar > .panel.details.css-style > .content > .computed .details-section > .content):
Ensure both top-level and nested details sections overwrite styles. CSS variables groups are in nested details sections.

(.sidebar > .panel.details.css-style > .content > .computed .details-section.computed-style-variables .computed-property-item):
Adapt stylesheet to account for using `WI.ComputedStyleSection` instead of `WI.SpreadsheetCSSStyleDeclarationEditor`

(.sidebar > .panel.details.css-style > .content > .computed > .details-section.computed-style-variables .scope-bar.computed-style-variables-grouping-mode):
(.sidebar > .panel.details.css-style > .content > .computed > .details-section.computed-style-variables .scope-bar.computed-style-variables-grouping-mode.default-item-selected:not(:hover)):
(.sidebar > .panel.details.css-style > .content > .computed > .details-section.computed-style-variables .scope-bar.computed-style-variables-grouping-mode.default-item-selected:hover):
Reuse the visual treatment from SourcesNavigationSidebarPanel.css to avoid highlighting the default grouping mode scope bar item.

(.sidebar > .panel.details.css-style > .content > .computed > .details-section > .content): Deleted.
(.sidebar > .panel.details.css-style > .content > .computed .property): Deleted.

(WI.ComputedStyleDetailsPanel.prototype.layout):
Skip destroying and rebuilding sections whose data sources change during
`WI.ComputedStyleDetailsPanel.refresh()` and which handle layout internally.
We need to remove and rebuild just the sections for CSS variables because
layout is requested in response to changing the CSS variables grouping mode.

(WI.ComputedStyleDetailsPanel.prototype._createVariablesStyleSection):
Replaces the use of `WI.SpreadsheetCSSStyleDeclarationEditor` for rendering CSS variables with
`WI.ComputedStyleSection` which is already used for rendering CSS properties.
It's a lighter-weight View that's fit for purpose.

(WI.ComputedStyleDetailsPanel.prototype._renderVariablesStyleSectionGroup):
Use a generic renderer for CSS variable sections that can be reused for any group type.

(WI.ComputedStyleDetailsPanel.prototype._handleDetailsSectionCollapsedStateChanged):
Generalize handling collapsed state change events for all sections, current and future.

(WI.ComputedStyleDetailsPanel.prototype._handleEditorFilterApplied):
Generalize handling filtering events for all sections, current and future.

(WI.ComputedStyleDetailsPanel.prototype._handleVariablesGroupingModeScopeBarSelectionChanged):
(WI.ComputedStyleDetailsPanel.prototype._handleVariablesGroupingSettingChanged):
(WI.ComputedStyleDetailsPanel.prototype._handlePropertiesSectionCollapsedStateChanged): Deleted.
(WI.ComputedStyleDetailsPanel.prototype._handleVariablesSectionCollapsedStateChanged): Deleted.
* UserInterface/Views/ComputedStyleSection.js:
(WI.ComputedStyleSection):
Change the default value of `_styleTraces` to null instead of an empty array so that
`WI.ComputedStyleSection.layout()` doesn't attempt to access it like a `Map`.

LayoutTests:

Check logic for grouping CSS variables by value type in Web Inspector.

* inspector/css/variableStylesByType-expected.txt: Added.
* inspector/css/variableStylesByType.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (286875 => 286876)


--- trunk/LayoutTests/ChangeLog	2021-12-10 22:01:57 UTC (rev 286875)
+++ trunk/LayoutTests/ChangeLog	2021-12-10 22:15:52 UTC (rev 286876)
@@ -1,3 +1,16 @@
+2021-12-10  Razvan Caliman  <rcali...@apple.com>
+
+        Web Inspector: Computed Panel: Group CSS variables by value type
+        https://bugs.webkit.org/show_bug.cgi?id=233563
+        <rdar://82978905>
+
+        Reviewed by Devin Rousso and Patrick Angle.
+
+        Check logic for grouping CSS variables by value type in Web Inspector.
+
+        * inspector/css/variableStylesByType-expected.txt: Added.
+        * inspector/css/variableStylesByType.html: Added.
+
 2021-12-10  Chris Dumez  <cdu...@apple.com>
 
         Improve <type="datetime-local"> value parsing and sanitization

Added: trunk/LayoutTests/inspector/css/variableStylesByType-expected.txt (0 => 286876)


--- trunk/LayoutTests/inspector/css/variableStylesByType-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/inspector/css/variableStylesByType-expected.txt	2021-12-10 22:15:52 UTC (rev 286876)
@@ -0,0 +1,30 @@
+Test for DOMNodeStyles.variableStylesByType
+
+
+== Running test suite: DOMNodeStyles.variableStylesByType
+-- Running test case: DOMNodeStyles.variableStylesByType.Colors
+PASS: Should have "colors" variables group.
+PASS: Group should include all expected variables.
+
+-- Running test case: DOMNodeStyles.variableStylesByType.Dimensions
+PASS: Should have "dimensions" variables group.
+PASS: Group should include all expected variables.
+
+-- Running test case: DOMNodeStyles.variableStylesByType.Numbers
+PASS: Should have "numbers" variables group.
+PASS: Group should include all expected variables.
+
+-- Running test case: DOMNodeStyles.variableStylesByType.Other
+PASS: Should have "other" variables group.
+PASS: Group should include all expected variables.
+
+-- Running test case: DOMNodeStyles.variableStylesByType.Mixed
+PASS: Should have "colors" variables group.
+PASS: Group should include all expected variables.
+PASS: Should have "dimensions" variables group.
+PASS: Group should include all expected variables.
+PASS: Should have "numbers" variables group.
+PASS: Group should include all expected variables.
+PASS: Should have "other" variables group.
+PASS: Group should include all expected variables.
+

Added: trunk/LayoutTests/inspector/css/variableStylesByType.html (0 => 286876)


--- trunk/LayoutTests/inspector/css/variableStylesByType.html	                        (rev 0)
+++ trunk/LayoutTests/inspector/css/variableStylesByType.html	2021-12-10 22:15:52 UTC (rev 286876)
@@ -0,0 +1,181 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script>
+
+function test()
+{
+    let suite = InspectorTest.createAsyncSuite("DOMNodeStyles.variableStylesByType");
+
+    function addTestCase({name, description, selector, expected})
+    {
+        suite.addTestCase({
+            name,
+            description,
+            async test() {
+                let documentNode = await WI.domManager.requestDocument();
+                let nodeId = await documentNode.querySelector(selector);
+                let domNode = await WI.domManager.nodeForId(nodeId);
+                InspectorTest.assert(domNode, `Should find DOM Node for selector '${selector}'.`);
+
+                let cssStyles = WI.cssManager.stylesForNode(domNode);
+                await cssStyles.refreshIfNeeded();
+
+                let styleGroups = cssStyles.variableStylesByType;
+                for (let {groupType, variables} of expected) {
+                    let propertyNames = styleGroups.get(groupType).properties.map(property => property.name);
+                    InspectorTest.expectTrue(styleGroups.has(groupType), `Should have "${groupType}" variables group.`);
+                    InspectorTest.expectShallowEqual(propertyNames.sort(), variables.sort(), "Group should include all expected variables.");
+                }
+            },
+        });
+    }
+
+    addTestCase({
+        name: "DOMNodeStyles.variableStylesByType.Colors",
+        description: "Check that variables with color values are correctly identified.",
+        selector: "#colors",
+        expected: [
+            {
+                groupType: WI.DOMNodeStyles.VariablesGroupType.Colors,
+                variables: ["--colorName", "--colorHEX", "--colorRGB", "--colorRGBA", "--colorHSL", "--colorHSLA"],
+            },
+        ],
+    });
+
+    addTestCase({
+        name: "DOMNodeStyles.variableStylesByType.Dimensions",
+        description: "Check that variables with dimension values are correctly identified.",
+        selector: "#dimensions",
+        expected: [
+            {
+                groupType: WI.DOMNodeStyles.VariablesGroupType.Dimensions,
+                variables: ["--dimensionPX", "--dimensionPercent", "--dimensionFloat", "--dimensionNegative", "--dimensionNegativeFloat"],
+            },
+        ],
+    });
+
+    addTestCase({
+        name: "DOMNodeStyles.variableStylesByType.Numbers",
+        description: "Check that variables with number values are correctly identified.",
+        selector: "#numbers",
+        expected: [
+            {
+                groupType: WI.DOMNodeStyles.VariablesGroupType.Numbers,
+                variables: ["--number", "--numberFloat", "--numberNegative", "--numberNegativeFloat"],
+            },
+        ],
+    });
+
+    addTestCase({
+        name: "DOMNodeStyles.variableStylesByType.Other",
+        description: "Check that variables with values with unmatched types are placed in the fallback group.",
+        selector: "#other",
+        expected: [
+            {
+                groupType: WI.DOMNodeStyles.VariablesGroupType.Other,
+                variables: ["--shadow", "--font-family", "--font-shorthand", "--keyword"],
+            },
+        ],
+    });
+
+    addTestCase({
+        name: "DOMNodeStyles.variableStylesByType.Mixed",
+        description: "Check that all type groups have variables allocated.",
+        selector: "#mixed",
+        expected: [
+            {
+                groupType: WI.DOMNodeStyles.VariablesGroupType.Colors,
+                variables: ["--color"],
+            },
+            {
+                groupType: WI.DOMNodeStyles.VariablesGroupType.Dimensions,
+                variables: ["--dimension"],
+            },
+            {
+                groupType: WI.DOMNodeStyles.VariablesGroupType.Numbers,
+                variables: ["--number"],
+            },
+            {
+                groupType: WI.DOMNodeStyles.VariablesGroupType.Other,
+                variables: ["--font-family"],
+            },
+        ],
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+<style>
+
+div#colors {
+    --colorName: black;
+    --colorHEX: #000;
+    --colorRGB: rgb(0, 0, 0);
+    --colorRGBA: rgba(0, 0, 0, 1);
+    --colorHSL: hsl(0, 0%, 0%);
+    --colorHSLA: hsla(0, 0%, 0%, 1);
+
+    /* should not match any below */
+    --shadow: 1px 1px black;
+    --dimensionPX: 100px;
+    --number: 100;
+}
+
+div#dimensions {
+    --dimensionPX: 100px;
+    --dimensionPercent: 100%;
+    --dimensionFloat: 1.0%;
+    --dimensionNegative: -100px;
+    --dimensionNegativeFloat: -1.0%;
+
+    /* should not match any below */
+    --shadow: 1px 1px black;
+    --color: black;
+    --number: 100;
+    --numberFloat: 1.0;
+    --numberNegativeFloat: -1.0;
+}
+
+div#numbers {
+    --number: 100;
+    --numberFloat: 1.0;
+    --numberNegative: -100;
+    --numberNegativeFloat: -1.0;
+
+    /* should not match any below */
+    --shadow: 1px 1px black;
+    --dimensionPX: 100px;
+    --dimensionPercent: 100%;
+    --dimensionNegative: -100px;
+    --dimensionNegativeFloat: -1.0%;
+}
+
+div#other {
+    --shadow: 1px 1px black;
+    --font-family: sans-serif;
+    --font-shorthand: 16px / 1.6 sans-serif;
+    --keyword: inherit;
+}
+
+div#mixed {
+    --color: black;
+    --dimension: 100px;
+    --number: 100;
+    --font-family: sans-serif;
+}
+
+</style>
+</head>
+<body _onload_="runTest();">
+<p>Test for DOMNodeStyles.variableStylesByType</p>
+<div>
+    <div id="colors"></div>
+    <div id="dimensions"></div>
+    <div id="numbers"></div>
+    <div id="other"></div>
+    <div id="mixed"></div>
+</div>
+</body>
+</html>
\ No newline at end of file

Modified: trunk/Source/WebInspectorUI/ChangeLog (286875 => 286876)


--- trunk/Source/WebInspectorUI/ChangeLog	2021-12-10 22:01:57 UTC (rev 286875)
+++ trunk/Source/WebInspectorUI/ChangeLog	2021-12-10 22:15:52 UTC (rev 286876)
@@ -1,3 +1,87 @@
+2021-12-10  Razvan Caliman  <rcali...@apple.com>
+
+        Web Inspector: Computed Panel: Group CSS variables by value type
+        https://bugs.webkit.org/show_bug.cgi?id=233563
+        <rdar://82978905>
+
+        Reviewed by Devin Rousso and Patrick Angle.
+
+        Add the ability to view CSS variables in the Computed styles details sidebar panel
+        groupped by value type in a few sections: "colors", "dimensions", "numbers" and a
+        catch-all group of "other".
+
+        * Localizations/en.lproj/localizedStrings.js:
+        * UserInterface/Models/DOMNodeStyles.js:
+        (WI.DOMNodeStyles):
+        (WI.DOMNodeStyles.prototype.variableStylesByType):
+        Iterate on-demand over all CSS variables found in the node's computed styles
+        and assign each to a group depending on its value type:
+        - color
+        - dimension (number followed by a CSS unit-like string)
+        - number
+        - other
+
+        Additional groups and refinements will come in follow-up patches.
+
+        (WI.DOMNodeStyles.prototype.refresh.fetchedComputedStyle):
+        The map of CSS variable groups gets invalidated when there's a significant
+        change in the node's computed style. This supports the use case where previously
+        empty groups become populated or, conversely, become empty.
+
+        * UserInterface/Views/ComputedStyleDetailsPanel.js:
+        (WI.ComputedStyleDetailsPanel):
+        (WI.ComputedStyleDetailsPanel.prototype.refresh):
+        (WI.ComputedStyleDetailsPanel.prototype.applyFilter):
+        (WI.ComputedStyleDetailsPanel.prototype.initialLayout):
+        No longer generate the elements for laying out CSS variables during `initialLayout()`
+        but handle them during `layout()`. This support mutating the DOM for laying out
+        either one top-level list of CSS variables (ungrouped) or multiple lists of CSS variable
+        groups depeding on the grouping mode selected at runtime.
+
+        * UserInterface/Views/ComputedStyleDetailsPanel.css:
+        (.sidebar > .panel.details.css-style > .content > .computed .details-section > .content):
+        Ensure both top-level and nested details sections overwrite styles. CSS variables groups are in nested details sections.
+
+        (.sidebar > .panel.details.css-style > .content > .computed .details-section.computed-style-variables .computed-property-item):
+        Adapt stylesheet to account for using `WI.ComputedStyleSection` instead of `WI.SpreadsheetCSSStyleDeclarationEditor`
+
+        (.sidebar > .panel.details.css-style > .content > .computed > .details-section.computed-style-variables .scope-bar.computed-style-variables-grouping-mode):
+        (.sidebar > .panel.details.css-style > .content > .computed > .details-section.computed-style-variables .scope-bar.computed-style-variables-grouping-mode.default-item-selected:not(:hover)):
+        (.sidebar > .panel.details.css-style > .content > .computed > .details-section.computed-style-variables .scope-bar.computed-style-variables-grouping-mode.default-item-selected:hover):
+        Reuse the visual treatment from SourcesNavigationSidebarPanel.css to avoid highlighting the default grouping mode scope bar item.
+
+        (.sidebar > .panel.details.css-style > .content > .computed > .details-section > .content): Deleted.
+        (.sidebar > .panel.details.css-style > .content > .computed .property): Deleted.
+
+        (WI.ComputedStyleDetailsPanel.prototype.layout):
+        Skip destroying and rebuilding sections whose data sources change during
+        `WI.ComputedStyleDetailsPanel.refresh()` and which handle layout internally.
+        We need to remove and rebuild just the sections for CSS variables because
+        layout is requested in response to changing the CSS variables grouping mode.
+
+        (WI.ComputedStyleDetailsPanel.prototype._createVariablesStyleSection):
+        Replaces the use of `WI.SpreadsheetCSSStyleDeclarationEditor` for rendering CSS variables with
+        `WI.ComputedStyleSection` which is already used for rendering CSS properties.
+        It's a lighter-weight View that's fit for purpose.
+
+        (WI.ComputedStyleDetailsPanel.prototype._renderVariablesStyleSectionGroup):
+        Use a generic renderer for CSS variable sections that can be reused for any group type.
+
+        (WI.ComputedStyleDetailsPanel.prototype._handleDetailsSectionCollapsedStateChanged):
+        Generalize handling collapsed state change events for all sections, current and future.
+
+        (WI.ComputedStyleDetailsPanel.prototype._handleEditorFilterApplied):
+        Generalize handling filtering events for all sections, current and future.
+
+        (WI.ComputedStyleDetailsPanel.prototype._handleVariablesGroupingModeScopeBarSelectionChanged):
+        (WI.ComputedStyleDetailsPanel.prototype._handleVariablesGroupingSettingChanged):
+        (WI.ComputedStyleDetailsPanel.prototype._handlePropertiesSectionCollapsedStateChanged): Deleted.
+        (WI.ComputedStyleDetailsPanel.prototype._handleVariablesSectionCollapsedStateChanged): Deleted.
+        * UserInterface/Views/ComputedStyleSection.js:
+        (WI.ComputedStyleSection):
+        Change the default value of `_styleTraces` to null instead of an empty array so that
+        `WI.ComputedStyleSection.layout()` doesn't attempt to access it like a `Map`.
+
 2021-12-10  Nikita Vasilyev  <nvasil...@apple.com>
 
         Web Inspector: Add a swatch for align-items and align-self

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


--- trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js	2021-12-10 22:01:57 UTC (rev 286875)
+++ trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js	2021-12-10 22:15:52 UTC (rev 286876)
@@ -244,6 +244,8 @@
 localizedStrings["Busy"] = "Busy";
 localizedStrings["By Path"] = "By Path";
 localizedStrings["By Type"] = "By Type";
+/* Label for button to show CSS variables grouped by type */
+localizedStrings["By Type @ Computed Style variables grouping mode"] = "By Type";
 localizedStrings["Byte Range %s\u2013%s"] = "Byte Range %s\u2013%s";
 localizedStrings["Bytes Received"] = "Bytes Received";
 localizedStrings["Bytes Sent"] = "Bytes Sent";
@@ -340,6 +342,8 @@
 localizedStrings["Collapse blackboxed call frames @ Experimental Settings"] = "Collapse blackboxed call frames";
 localizedStrings["Collapse columns"] = "Collapse columns";
 localizedStrings["Collect garbage"] = "Collect garbage";
+/* Section header for the group of CSS variables with colors as values */
+localizedStrings["Colors @ Computed Style variables section"] = "Colors";
 localizedStrings["Comment"] = "Comment";
 /* Property value for `font-variant-ligatures: common-ligatures`. */
 localizedStrings["Common @ Font Details Sidebar Property Value"] = "Common";
@@ -469,6 +473,8 @@
 /* Property value for `font-variant-numeric: diagonal-fractions`. */
 localizedStrings["Diagonal Fractions @ Font Details Sidebar Property Value"] = "Diagonal Fractions";
 localizedStrings["Dimensions"] = "Dimensions";
+/* Section header for the group of CSS variables with dimensions as values */
+localizedStrings["Dimensions @ Computed style variables section"] = "Dimensions";
 localizedStrings["Disable Audit"] = "Disable Audit";
 localizedStrings["Disable Breakpoint"] = "Disable Breakpoint";
 localizedStrings["Disable Breakpoints"] = "Disable Breakpoints";
@@ -1007,6 +1013,8 @@
 localizedStrings["Not found"] = "Not found";
 /* Title of icon indicating that the selected audit has not been run yet. */
 localizedStrings["Not yet run @ Audit Tab - Test Case"] = "Not yet run";
+/* Section header for the group of CSS variables with numbers as values */
+localizedStrings["Numbers @ Computed Style variables section"] = "Numbers";
 /* Property title for `font-variant-numeric`. */
 localizedStrings["Numeric @ Font Details Sidebar Property"] = "Numeric";
 localizedStrings["Object Graph"] = "Object Graph";
@@ -1041,6 +1049,8 @@
 localizedStrings["Originally %s"] = "Originally %s";
 localizedStrings["Originator"] = "Originator";
 localizedStrings["Other"] = "Other";
+/* Section header for the generic group of CSS variables */
+localizedStrings["Other @ Computed Style variables section"] = "Other";
 localizedStrings["Other Issue"] = "Other Issue";
 localizedStrings["Other Threads"] = "Other Threads";
 localizedStrings["Other: %s"] = "Other: %s";
@@ -1589,6 +1599,10 @@
 /* Break (pause) on uncaught (unhandled) exceptions */
 localizedStrings["Uncaught Exceptions @ _javascript_ Breakpoint"] = "Uncaught Exceptions";
 localizedStrings["Undefined custom element"] = "Undefined custom element";
+/* Label for button to show CSS variables ungrouped */
+localizedStrings["Ungrouped @ Computed Style variables grouping mode"] = "Ungrouped";
+/* Section header for ungrouped CSS variables */
+localizedStrings["Ungrouped @ Computed Style variables section"] = "Ungrouped";
 /* Property value for `font-variant-capitals: unicase`. */
 localizedStrings["Unicase @ Font Details Sidebar Property Value"] = "Unicase";
 localizedStrings["Unique"] = "Unique";

Modified: trunk/Source/WebInspectorUI/UserInterface/Models/DOMNodeStyles.js (286875 => 286876)


--- trunk/Source/WebInspectorUI/UserInterface/Models/DOMNodeStyles.js	2021-12-10 22:01:57 UTC (rev 286875)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/DOMNodeStyles.js	2021-12-10 22:15:52 UTC (rev 286876)
@@ -48,6 +48,7 @@
         this._propertyNameToEffectivePropertyMap = {};
         this._usedCSSVariables = new Set;
         this._allCSSVariables = new Set;
+        this._variableStylesByType = null;
 
         this._pendingRefreshTask = null;
         this.refresh();
@@ -144,6 +145,67 @@
         return WI.DOMNodeStyles.uniqueOrderedStyles(this._orderedStyles);
     }
 
+    get variableStylesByType()
+    {
+        if (this._variableStylesByType)
+            return this._variableStylesByType;
+
+        let properties = this._computedStyle?.properties;
+        if (!properties)
+            return new Map;
+
+        // Will iterate in order through type checkers for each CSS variable to identify its type.
+        // The catch-all "other" must always be last.
+        const typeCheckFunctions = [
+            {
+                type: WI.DOMNodeStyles.VariablesGroupType.Colors,
+                checker: (property) => WI.Color.fromString(property.value),
+            },
+            {
+                type: WI.DOMNodeStyles.VariablesGroupType.Dimensions,
+                // FIXME: <https://webkit.org/b/233576> build RegExp from `WI.CSSCompletions.lengthUnits`.
+                checker: (property) => /^-?\d+(\.\d+)?\D+$/.test(property.value),
+            },
+            {
+                type: WI.DOMNodeStyles.VariablesGroupType.Numbers,
+                checker: (property) => /^-?\d+(\.\d+)?$/.test(property.value),
+            },
+            {
+                type: WI.DOMNodeStyles.VariablesGroupType.Other,
+                checker: (property) => true,
+            },
+        ];
+
+        let variablesForType = {};
+        for (let property of properties) {
+            if (!property.isVariable)
+                continue;
+
+            for (let {type, checker} of typeCheckFunctions) {
+                if (checker(property)) {
+                    variablesForType[type] ||= [];
+                    variablesForType[type].push(property);
+                    break;
+                }
+            }
+        }
+
+        this._variableStylesByType = new Map;
+        for (let {type} of typeCheckFunctions) {
+            if (!variablesForType[type]?.length)
+                continue;
+
+            const ownerStyleSheet = null;
+            const id = null;
+            const inherited = false;
+            const text = null;
+            let style = new WI.CSSStyleDeclaration(this, ownerStyleSheet, id, WI.CSSStyleDeclaration.Type.Computed, this._node, inherited, text, variablesForType[type]);
+            this._variableStylesByType.set(type, style);
+        }
+
+        return this._variableStylesByType;
+    }
+
     refreshIfNeeded()
     {
         if (this._pendingRefreshTask)
@@ -315,6 +377,9 @@
                 }
             }
 
+            if (significantChange)
+                this._variableStylesByType = null;
+
             this._previousStylesMap = null;
             this._includeUserAgentRulesOnNextRefresh = false;
 
@@ -992,3 +1057,11 @@
     NeedsRefresh: "dom-node-styles-needs-refresh",
     Refreshed: "dom-node-styles-refreshed"
 };
+
+WI.DOMNodeStyles.VariablesGroupType = {
+    Ungrouped: "ungrouped",
+    Colors: "colors",
+    Dimensions: "dimensions",
+    Numbers: "numbers",
+    Other: "other",
+};

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ComputedStyleDetailsPanel.css (286875 => 286876)


--- trunk/Source/WebInspectorUI/UserInterface/Views/ComputedStyleDetailsPanel.css	2021-12-10 22:01:57 UTC (rev 286875)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ComputedStyleDetailsPanel.css	2021-12-10 22:15:52 UTC (rev 286876)
@@ -32,7 +32,8 @@
     background-color: var(--background-color);
 }
 
-.sidebar > .panel.details.css-style > .content > .computed > .details-section > .content {
+/* Ensure that font is overwritten for both top-level and nested details sections. */
+.sidebar > .panel.details.css-style > .content > .computed .details-section > .content {
     font: 12px -webkit-system-font, sans-serif;
 }
 
@@ -45,8 +46,7 @@
     padding-bottom: 3px;
 }
 
-.sidebar > .panel.details.css-style > .content > .computed .property {
-    position: relative;
+.sidebar > .panel.details.css-style > .content > .computed .details-section.computed-style-variables .computed-property-item {
     text-indent: -13px; /* width of "--" in `11px Menlo` */
 }
 
@@ -59,3 +59,18 @@
 .sidebar > .panel.details.css-style > .content > .computed > .details-section.computed-style-variables .property:not(:hover) .go-to-arrow {
     display: none;
 }
+
+.sidebar > .panel.details.css-style > .content > .computed > .details-section.computed-style-variables .scope-bar.computed-style-variables-grouping-mode {
+    margin-top: -1px;
+    font-weight: normal;
+}
+
+.sidebar > .panel.details.css-style > .content > .computed > .details-section.computed-style-variables .scope-bar.computed-style-variables-grouping-mode.default-item-selected:not(:hover) {
+    --scope-bar-text-color-override: var(--text-color);
+    --scope-bar-background-color-override: transparent;
+    --scope-bar-border-color-override: transparent;
+}
+
+.sidebar > .panel.details.css-style > .content > .computed > .details-section.computed-style-variables .scope-bar.computed-style-variables-grouping-mode.default-item-selected:hover {
+    --scope-bar-background-opacity-override: 0.5;
+}
\ No newline at end of file

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ComputedStyleDetailsPanel.js (286875 => 286876)


--- trunk/Source/WebInspectorUI/UserInterface/Views/ComputedStyleDetailsPanel.js	2021-12-10 22:01:57 UTC (rev 286875)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ComputedStyleDetailsPanel.js	2021-12-10 22:15:52 UTC (rev 286876)
@@ -33,8 +33,32 @@
         this._computedStylePreferShorthandsSetting = new WI.Setting("computed-style-use-shorthands", false);
 
         this._filterText = null;
+        this._detailsSectionByStyleSectionMap = new Map;
+        this._variablesStyleSectionForGroupTypeMap = new Map;
     }
 
+    // Static
+
+    static displayNameForVariablesGroupType(variablesGroupType)
+    {
+        switch (variablesGroupType) {
+        case WI.DOMNodeStyles.VariablesGroupType.Colors:
+            return WI.UIString("Colors", "Colors @ Computed Style variables section", "Section header for the group of CSS variables with colors as values");
+
+        case WI.DOMNodeStyles.VariablesGroupType.Dimensions:
+            return WI.UIString("Dimensions", "Dimensions @ Computed style variables section", "Section header for the group of CSS variables with dimensions as values");
+
+        case WI.DOMNodeStyles.VariablesGroupType.Numbers:
+            return WI.UIString("Numbers", "Numbers @ Computed Style variables section", "Section header for the group of CSS variables with numbers as values");
+
+        case WI.DOMNodeStyles.VariablesGroupType.Other:
+            return WI.UIString("Other", "Other @ Computed Style variables section", "Section header for the generic group of CSS variables");
+        }
+
+        console.assert(false, "Unknown group type", variablesGroupType);
+        return "";
+    }
+
     // Public
 
     get minimumWidth()
@@ -42,6 +66,12 @@
         return this._boxModelDiagramRow?.minimumWidth ?? 0;
     }
 
+    get variablesGroupingMode()
+    {
+        console.assert(this._variablesGroupingModeScopeBar.selectedItems[0], "No selected variables grouping mode", this._variablesGroupingModeScopeBar.selectedItems);
+        return this._variablesGroupingModeScopeBar.selectedItems[0].id;
+    }
+
     refresh(significantChange)
     {
         // We only need to do a rebuild on significant changes. Other changes are handled
@@ -54,12 +84,33 @@
         this._computedStyleSection.styleTraces = this._computePropertyTraces(this.nodeStyles.uniqueOrderedStyles);
         this._computedStyleSection.style = this.nodeStyles.computedStyle;
         this._propertiesSection.element.classList.toggle("hidden", !this._computedStyleSection.propertiesToRender.length);
+        this._boxModelDiagramRow.nodeStyles = this.nodeStyles;
 
-        this._variablesTextEditor.style = this.nodeStyles.computedStyle;
-        this._variablesSection.element.classList.toggle("hidden", !this._variablesTextEditor.propertiesToRender.length);
+        let styleGroups = new Map;
 
-        this._boxModelDiagramRow.nodeStyles = this.nodeStyles;
+        switch (this.variablesGroupingMode) {
+        case WI.ComputedStyleDetailsPanel.VariablesGroupingMode.Ungrouped:
+            styleGroups.set(this._variablesStyleSectionForGroupTypeMap.get(WI.DOMNodeStyles.VariablesGroupType.Ungrouped), this.nodeStyles.computedStyle);
+            break;
 
+        case WI.ComputedStyleDetailsPanel.VariablesGroupingMode.ByType:
+            styleGroups = this.nodeStyles.variableStylesByType;
+            break;
+        }
+
+        for (let [type, style] of styleGroups) {
+            let variablesStyleSection = this._variablesStyleSectionForGroupTypeMap.get(type);
+            if (!variablesStyleSection) {
+                this.needsLayout();
+                break;
+            }
+
+            variablesStyleSection.style = style;
+
+            let detailsSection = this._detailsSectionByStyleSectionMap.get(variablesStyleSection);
+            detailsSection.element.classList.toggle("hidden", !variablesStyleSection.propertiesToRender.length);
+        }
+
         if (this._filterText)
             this.applyFilter(this._filterText);
 
@@ -73,8 +124,8 @@
         if (!this.didInitialLayout)
             return;
 
-        this._computedStyleSection.applyFilter(filterText);
-        this._variablesTextEditor.applyFilter(filterText);
+        for (let styleSection of this._detailsSectionByStyleSectionMap.keys())
+            styleSection.applyFilter(filterText);
     }
 
     // SpreadsheetCSSStyleDeclarationEditor delegate
@@ -118,28 +169,27 @@
         let propertiesRow = new WI.DetailsSectionRow;
         let propertiesGroup = new WI.DetailsSectionGroup([propertiesRow]);
         this._propertiesSection = new WI.DetailsSection("computed-style-properties", WI.UIString("Properties"), [propertiesGroup], propertyFiltersElement);
-        this._propertiesSection.addEventListener(WI.DetailsSection.Event.CollapsedStateChanged, this._handlePropertiesSectionCollapsedStateChanged, this);
+        this._propertiesSection.addEventListener(WI.DetailsSection.Event.CollapsedStateChanged, this._handleDetailsSectionCollapsedStateChanged, this);
 
         this.addSubview(this._computedStyleSection);
 
         propertiesRow.element.appendChild(this._computedStyleSection.element);
+        this._detailsSectionByStyleSectionMap.set(this._computedStyleSection, this._propertiesSection);
 
-        this._variablesTextEditor = new WI.SpreadsheetCSSStyleDeclarationEditor(this);
-        this._variablesTextEditor.propertyVisibilityMode = WI.SpreadsheetCSSStyleDeclarationEditor.PropertyVisibilityMode.HideNonVariables;
-        this._variablesTextEditor.hideFilterNonMatchingProperties = true;
-        this._variablesTextEditor.sortPropertiesByName = true;
-        this._variablesTextEditor.addEventListener(WI.SpreadsheetCSSStyleDeclarationEditor.Event.FilterApplied, this._handleEditorFilterApplied, this);
-        this._variablesTextEditor.element.dir = "ltr";
+        let variablesGroupingModeScopeBarItems = [
+            new WI.ScopeBarItem(WI.ComputedStyleDetailsPanel.VariablesGroupingMode.Ungrouped, WI.UIString("Ungrouped", "Ungrouped @ Computed Style variables grouping mode", "Label for button to show CSS variables ungrouped")),
+            new WI.ScopeBarItem(WI.ComputedStyleDetailsPanel.VariablesGroupingMode.ByType, WI.UIString("By Type", "By Type @ Computed Style variables grouping mode", "Label for button to show CSS variables grouped by type"))
+        ];
 
-        let variablesRow = new WI.DetailsSectionRow;
-        let variablesGroup = new WI.DetailsSectionGroup([variablesRow]);
-        this._variablesSection = new WI.DetailsSection("computed-style-variables", WI.UIString("Variables"), [variablesGroup]);
-        this._variablesSection.addEventListener(WI.DetailsSection.Event.CollapsedStateChanged, this._handleVariablesSectionCollapsedStateChanged, this);
+        const shouldGroupNonExclusiveItems = true;
+        this._variablesGroupingModeScopeBar = new WI.ScopeBar("computed-style-variables-grouping-mode", variablesGroupingModeScopeBarItems, variablesGroupingModeScopeBarItems[0], shouldGroupNonExclusiveItems);
+        this._variablesGroupingModeScopeBar.addEventListener(WI.ScopeBar.Event.SelectionChanged, this._handleVariablesGroupingModeScopeBarSelectionChanged, this);
 
-        this.addSubview(this._variablesTextEditor);
+        this._variablesRow = new WI.DetailsSectionRow;
+        let variablesGroup = new WI.DetailsSectionGroup([this._variablesRow]);
+        this._variablesSection = new WI.DetailsSection("computed-style-variables", WI.UIString("Variables"), [variablesGroup], this._variablesGroupingModeScopeBar.element);
+        this._variablesSection.addEventListener(WI.DetailsSection.Event.CollapsedStateChanged, this._handleDetailsSectionCollapsedStateChanged, this);
 
-        variablesRow.element.appendChild(this._variablesTextEditor.element);
-
         this.element.appendChild(this._propertiesSection.element);
         this.element.appendChild(this._variablesSection.element);
 
@@ -147,6 +197,46 @@
         this._computedStylePreferShorthandsSetting.addEventListener(WI.Setting.Event.Changed, this._handleUseShorthandsSettingChanged, this);
     }
 
+    layout()
+    {
+        super.layout();
+
+        for (let [styleSection, detailsSection] of this._detailsSectionByStyleSectionMap) {
+            // The details section for computed properties is updated in-place by WI.ComputedStyleDetailsPanel.refresh().
+            if (styleSection === this._computedStyleSection)
+                continue;
+
+            // WI.ComputedStyleSection is a view but its element is attached to a WI.DetailsSection which isn't. Reparent it before removing.
+            // FIXME: <https://webkit.org/b/152269> - Web Inspector: Convert DetailsSection classes to use View
+            this.element.appendChild(styleSection.element);
+            this.removeSubview(styleSection);
+            styleSection.element.remove();
+
+            // The top-level details section for variables needs to be preserved because it's the host of nested details sections for variables groups.
+            if (detailsSection === this._variablesSection)
+                continue;
+
+            detailsSection.element.remove();
+            this._detailsSectionByStyleSectionMap.delete(styleSection);
+        }
+
+        this._variablesStyleSectionForGroupTypeMap.clear();
+
+        switch (this.variablesGroupingMode) {
+        case WI.ComputedStyleDetailsPanel.VariablesGroupingMode.Ungrouped:
+            this._renderVariablesStyleSectionGroup(this.nodeStyles.computedStyle, WI.DOMNodeStyles.VariablesGroupType.Ungrouped);
+            break;
+
+        case WI.ComputedStyleDetailsPanel.VariablesGroupingMode.ByType:
+            for (let [type, style] of this.nodeStyles.variableStylesByType)
+                this._renderVariablesStyleSectionGroup(style, type, WI.ComputedStyleDetailsPanel.displayNameForVariablesGroupType(type));
+            break;
+        }
+
+        if (this._filterText)
+            this.applyFilter(this._filterText);
+    }
+
     filterDidChange(filterBar)
     {
         this.applyFilter(filterBar.filters.text);
@@ -154,6 +244,35 @@
 
     // Private
 
+    _renderVariablesStyleSectionGroup(style, groupType, label)
+    {
+        let variablesStyleSection = new WI.ComputedStyleSection(this);
+        variablesStyleSection.propertyVisibilityMode = WI.ComputedStyleSection.PropertyVisibilityMode.HideNonVariables;
+        variablesStyleSection.hideFilterNonMatchingProperties = true;
+        variablesStyleSection.addEventListener(WI.ComputedStyleSection.Event.FilterApplied, this._handleEditorFilterApplied, this);
+        variablesStyleSection.element.dir = "ltr";
+        variablesStyleSection.style = style;
+
+        this.addSubview(variablesStyleSection);
+
+        let detailsSection;
+        if (!label) {
+            this._variablesRow.element.appendChild(variablesStyleSection.element);
+            detailsSection = this._variablesSection;
+        } else {
+            let detailsSectionRow = new WI.DetailsSectionRow;
+            let detailsSectionGroup = new WI.DetailsSectionGroup([detailsSectionRow]);
+            detailsSection = new WI.DetailsSection(`computed-style-variables-group-${groupType}`, label, [detailsSectionGroup]);
+            detailsSection.addEventListener(WI.DetailsSection.Event.CollapsedStateChanged, this._handleDetailsSectionCollapsedStateChanged, this);
+
+            detailsSectionRow.element.appendChild(variablesStyleSection.element);
+            this._variablesRow.element.appendChild(detailsSection.element);
+        }
+
+        this._detailsSectionByStyleSectionMap.set(variablesStyleSection, detailsSection);
+        this._variablesStyleSectionForGroupTypeMap.set(groupType, variablesStyleSection);
+    }
+
     _computePropertyTraces(orderedDeclarations)
     {
         let result = new Map();
@@ -171,28 +290,23 @@
         return result;
     }
 
-    _handlePropertiesSectionCollapsedStateChanged(event)
+    _handleEditorFilterApplied(event)
     {
-        if (event && event.data && !event.data.collapsed)
-            this._computedStyleSection.needsLayout();
+        let section = this._detailsSectionByStyleSectionMap.get(event.target);
+        section?.element.classList.toggle("hidden", !event.data.matches);
     }
 
-    _handleVariablesSectionCollapsedStateChanged(event)
+    _handleDetailsSectionCollapsedStateChanged(event)
     {
-        if (event && event.data && !event.data.collapsed)
-            this._variablesTextEditor.needsLayout();
-    }
+        if (event.data.collapsed)
+            return;
 
-    _handleEditorFilterApplied(event)
-    {
-        let section = null;
-        if (event.target === this._computedStyleSection)
-            section = this._propertiesSection;
-        else if (event.target === this._variablesTextEditor)
-            section = this._variablesSection;
-
-        if (section)
-            section.element.classList.toggle("hidden", !event.data.matches);
+        for (let [styleSection, detailsSection] of this._detailsSectionByStyleSectionMap) {
+            if (event.target === detailsSection) {
+                styleSection.needsLayout();
+                return;
+            }
+        }
     }
 
     _handleShowAllSettingChanged(event)
@@ -204,6 +318,15 @@
     {
         this._computedStyleSection.showsShorthandsInsteadOfLonghands = this._computedStylePreferShorthandsSetting.value;
     }
+
+    _handleVariablesGroupingModeScopeBarSelectionChanged(event)
+    {
+        this.needsLayout();
+    }
 };
 
 WI.ComputedStyleDetailsPanel.StyleClassName = "computed";
+WI.ComputedStyleDetailsPanel.VariablesGroupingMode = {
+    Ungrouped: "ungrouped",
+    ByType: "by-type",
+};

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ComputedStyleSection.js (286875 => 286876)


--- trunk/Source/WebInspectorUI/UserInterface/Views/ComputedStyleSection.js	2021-12-10 22:01:57 UTC (rev 286875)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ComputedStyleSection.js	2021-12-10 22:15:52 UTC (rev 286876)
@@ -34,7 +34,7 @@
 
         this._delegate = delegate;
         this._style = null;
-        this._styleTraces = [];
+        this._styleTraces = null;
         this._propertyViews = [];
 
         this._showsImplicitProperties = false;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to