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

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

<h3>Log Message</h3>
<pre>Web Inspector: Styles: format style declarations after editing
https://bugs.webkit.org/show_bug.cgi?id=178835
<rdar://problem/35185060>

Reviewed by Devin Rousso.

Source/WebInspectorUI:

Indent CSS properties with spaces/tabs set in Web Inspector settings. Increse indentation level when CSS rules are
inside of at-rules (e.g. @media, @keyframes, @supports).

Don't indent CSS properties in style attributes. Keep them on the single line, separated by a space character:

    style="font-size: 12px; color: black;"

* UserInterface/Models/CSSProperty.js:
(WI.CSSProperty.prototype.set text):
Introduce `_isTextPendingSave` flag. It's needed when saving pasted text, and saving commented out or uncommented CSS properties.

(WI.CSSProperty.prototype.get formattedText):

(WI.CSSProperty.prototype.replaceWithText): Deleted.
This is redundant - setting `text` works the same.

(WI.CSSProperty.prototype._updateStyleText):
(WI.CSSProperty.prototype._updateOwnerStyleText):
(WI.CSSProperty.prototype._prependSemicolonIfNeeded): Deleted.
Greatly simplify the logic now that we save formatted text and don't modify styleText.

* UserInterface/Models/CSSStyleDeclaration.js:
(WI.CSSStyleDeclaration.prototype.removeProperty):
Remode unnecessary code that modifies `_styleSheetTextRange`. The backend sends new `_styleSheetTextRange` data
upon a change.

(WI.CSSStyleDeclaration.prototype.generateFormattedText): Renamed from 'generateCSSRuleString'.

* UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js:
(WI.SpreadsheetCSSStyleDeclarationSection.prototype._populateIconElementContextMenu):
* UserInterface/Views/SpreadsheetStyleProperty.js:
(WI.SpreadsheetStyleProperty.prototype.remove):

LayoutTests:

- Indent text of expected CSS properties.
- Test generateFormattedText with all permutations of its options.
- Add a basic test ensuring styleSheetTextRange updates correctly from the backend.

* inspector/css/add-css-property.html:
* inspector/css/generateCSSRuleString-expected.txt: Removed.
* inspector/css/generateCSSRuleString.html: Removed.
* inspector/css/generateFormattedText-expected.txt: Added.
* inspector/css/generateFormattedText.html: Added.
* inspector/css/modify-css-property-expected.txt:
* inspector/css/modify-css-property.html:
* inspector/css/modify-inline-style-expected.txt:
* inspector/css/resources/modify-css-property.css: Added.
(.rule-a):
(.rule-b):
(.rule-c):
(.rule-d):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsinspectorcssaddcsspropertyhtml">trunk/LayoutTests/inspector/css/add-css-property.html</a></li>
<li><a href="#trunkLayoutTestsinspectorcssmodifycsspropertyexpectedtxt">trunk/LayoutTests/inspector/css/modify-css-property-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorcssmodifycsspropertyhtml">trunk/LayoutTests/inspector/css/modify-css-property.html</a></li>
<li><a href="#trunkLayoutTestsinspectorcssmodifyinlinestyleexpectedtxt">trunk/LayoutTests/inspector/css/modify-inline-style-expected.txt</a></li>
<li><a href="#trunkSourceWebInspectorUIChangeLog">trunk/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsCSSPropertyjs">trunk/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsCSSStyleDeclarationjs">trunk/Source/WebInspectorUI/UserInterface/Models/CSSStyleDeclaration.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetCSSStyleDeclarationSectionjs">trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetStylePropertyjs">trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsinspectorcssgenerateFormattedTextexpectedtxt">trunk/LayoutTests/inspector/css/generateFormattedText-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorcssgenerateFormattedTexthtml">trunk/LayoutTests/inspector/css/generateFormattedText.html</a></li>
<li><a href="#trunkLayoutTestsinspectorcssresourcesmodifycsspropertycss">trunk/LayoutTests/inspector/css/resources/modify-css-property.css</a></li>
</ul>

<h3>Removed Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsinspectorcssgenerateCSSRuleStringexpectedtxt">trunk/LayoutTests/inspector/css/generateCSSRuleString-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorcssgenerateCSSRuleStringhtml">trunk/LayoutTests/inspector/css/generateCSSRuleString.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (283722 => 283723)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog      2021-10-07 18:08:53 UTC (rev 283722)
+++ trunk/LayoutTests/ChangeLog 2021-10-07 18:15:14 UTC (rev 283723)
</span><span class="lines">@@ -1,3 +1,29 @@
</span><ins>+2021-10-07  Nikita Vasilyev  <nvasilyev@apple.com>
+
+        Web Inspector: Styles: format style declarations after editing
+        https://bugs.webkit.org/show_bug.cgi?id=178835
+        <rdar://problem/35185060>
+
+        Reviewed by Devin Rousso.
+
+        - Indent text of expected CSS properties.
+        - Test generateFormattedText with all permutations of its options.
+        - Add a basic test ensuring styleSheetTextRange updates correctly from the backend.
+
+        * inspector/css/add-css-property.html:
+        * inspector/css/generateCSSRuleString-expected.txt: Removed.
+        * inspector/css/generateCSSRuleString.html: Removed.
+        * inspector/css/generateFormattedText-expected.txt: Added.
+        * inspector/css/generateFormattedText.html: Added.
+        * inspector/css/modify-css-property-expected.txt:
+        * inspector/css/modify-css-property.html:
+        * inspector/css/modify-inline-style-expected.txt:
+        * inspector/css/resources/modify-css-property.css: Added.
+        (.rule-a):
+        (.rule-b):
+        (.rule-c):
+        (.rule-d):
+
</ins><span class="cx"> 2021-10-07  Ayumi Kojima  <ayumi_kojima@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [ BigSur AS EWS ] http/tests/xmlhttprequest/access-control-response-with-body.html is flaky crashing.
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorcssaddcsspropertyhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/css/add-css-property.html (283722 => 283723)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/css/add-css-property.html    2021-10-07 18:08:53 UTC (rev 283722)
+++ trunk/LayoutTests/inspector/css/add-css-property.html       2021-10-07 18:15:14 UTC (rev 283723)
</span><span class="lines">@@ -27,7 +27,7 @@
</span><span class="cx">             newProperty.name = "color";
</span><span class="cx">             newProperty.rawValue = "green";
</span><span class="cx"> 
</span><del>-            InspectorTest.expectEqual(styleDeclaration.text, `font-size: 14px;color: green;`, "`color: green` property should be appended.");
</del><ins>+            InspectorTest.expectEqual(styleDeclaration.text, `\n    font-size: 14px;\n    color: green;\n`, "`color: green` property should be appended.");
</ins><span class="cx"> 
</span><span class="cx">             styleDeclaration.locked = false;
</span><span class="cx">             resolve();
</span><span class="lines">@@ -43,7 +43,7 @@
</span><span class="cx">             newProperty.name = "color";
</span><span class="cx">             newProperty.rawValue = "green";
</span><span class="cx"> 
</span><del>-            let expected = `color: green;\n    outline: 2px solid brown;\n`;
</del><ins>+            let expected = `\n    color: green;\n    outline: 2px solid brown;\n`;
</ins><span class="cx">             InspectorTest.expectEqual(styleDeclaration.text, expected, "`color: green` property should be inserted before the first property.");
</span><span class="cx"> 
</span><span class="cx">             styleDeclaration = getMatchedStyle(".rule-b", resolve);
</span><span class="lines">@@ -51,7 +51,7 @@
</span><span class="cx">             newProperty.name = "display";
</span><span class="cx">             newProperty.rawValue = "block";
</span><span class="cx"> 
</span><del>-            expected = `color: green;\n    outline: 2px solid brown;display: block;\n`;
</del><ins>+            expected = `\n    color: green;\n    outline: 2px solid brown;\n    display: block;\n`;
</ins><span class="cx">             InspectorTest.expectEqual(styleDeclaration.text, expected, "`display: block` property should be appended at the end.");
</span><span class="cx"> 
</span><span class="cx">             styleDeclaration.locked = false;
</span><span class="lines">@@ -68,7 +68,7 @@
</span><span class="cx">             newProperty.name = "font-family";
</span><span class="cx">             newProperty.rawValue = "fantasy";
</span><span class="cx"> 
</span><del>-            let expected = `\n    background-color: peachpuff;font-family: fantasy;\n    color: burlywood\n`;
</del><ins>+            let expected = `\n    background-color: peachpuff;\n    font-family: fantasy;\n    color: burlywood;\n`;
</ins><span class="cx">             InspectorTest.expectEqual(styleDeclaration.text, expected, "`font-family: fantasy` property should be inserted after the first property.");
</span><span class="cx"> 
</span><span class="cx">             styleDeclaration.locked = false;
</span><span class="lines">@@ -85,7 +85,7 @@
</span><span class="cx">             newProperty.name = "display";
</span><span class="cx">             newProperty.rawValue = "inline";
</span><span class="cx"> 
</span><del>-            const expected = `\n    font-size: 14px;\n    /* color: red; */display: inline;\n`;
</del><ins>+            const expected = `\n    font-size: 14px;\n    /* color: red; */\n    display: inline;\n`;
</ins><span class="cx">             InspectorTest.expectEqual(styleDeclaration.text, expected, "`display: inline` property should be appended.");
</span><span class="cx"> 
</span><span class="cx">             styleDeclaration.locked = false;
</span><span class="lines">@@ -102,7 +102,7 @@
</span><span class="cx">             newProperty.name = "display";
</span><span class="cx">             newProperty.rawValue = "inline";
</span><span class="cx"> 
</span><del>-            const expected = `\n    font-size: 14px;\n    /* color: red */display: inline;\n`;
</del><ins>+            const expected = `\n    font-size: 14px;\n    /* color: red; */\n    display: inline;\n`;
</ins><span class="cx">             InspectorTest.expectEqual(styleDeclaration.text, expected, "`display: inline` property should be appended.");
</span><span class="cx"> 
</span><span class="cx">             styleDeclaration.locked = false;
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorcssgenerateCSSRuleStringexpectedtxt"></a>
<div class="delfile"><h4>Deleted: trunk/LayoutTests/inspector/css/generateCSSRuleString-expected.txt (283722 => 283723)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/css/generateCSSRuleString-expected.txt       2021-10-07 18:08:53 UTC (rev 283722)
+++ trunk/LayoutTests/inspector/css/generateCSSRuleString-expected.txt  2021-10-07 18:15:14 UTC (rev 283723)
</span><span class="lines">@@ -1,87 +0,0 @@
</span><del>-Testing that generated CSS rule strings have proper formatting.
-
-
-== Running test suite: CSS.generateCSSRuleString
--- Running test case: CSS.generateCSSRuleString.InlineStyle
-#test-node {
-    a: 1;
-    b: 2;
-    c: 3;
-}
-
--- Running test case: CSS.generateCSSRuleString.InlineStyle.WithCommentedProperty
-#test-node {
-    a: 1;
-    /* b: 2; */
-    c: 3;
-}
-
--- Running test case: CSS.generateCSSRuleString.MatchedRules
-@media only screen and (min-width: 0px) {
-    @media only screen and (min-height: 0px) {
-        body > div#test-node {
-            a: 1;
-            b: 2;
-            c: 3;
-        }
-    }
-}
-@media only screen and (min-width: 0px) {
-    body > #test-node {
-        a: 1;
-        b: 2;
-        c: 3;
-    }
-}
-body > div {
-    a: 1;
-    b: 2;
-    c: 3;
-}
-body > * {
-    a: 1;
-    b: 2;
-    c: 3;
-}
-* {
-    a: 1;
-    b: 2;
-    c: 3;
-}
-address, article, aside, div, footer, header, hgroup, layer, main, nav, section {
-    display: block;
-}
-
--- Running test case: CSS.generateCSSRuleString.MatchedRules.WithCommentedProperties
-@media only screen and (min-width: 0px) {
-    @media only screen and (min-height: 0px) {
-        body > div#test-node {
-            a: 1;
-            /* b: 2; */
-            c: 3;
-        }
-    }
-}
-@media only screen and (min-width: 0px) {
-    body > #test-node {
-        a: 1;
-        /* b: 2; */
-        c: 3;
-    }
-}
-body > div {
-    a: 1;
-    /* b: 2; */
-    c: 3;
-}
-body > * {
-    a: 1;
-    /* b: 2; */
-    c: 3;
-}
-* {
-    a: 1;
-    /* b: 2; */
-    c: 3;
-}
-
</del></span></pre></div>
<a id="trunkLayoutTestsinspectorcssgenerateCSSRuleStringhtml"></a>
<div class="delfile"><h4>Deleted: trunk/LayoutTests/inspector/css/generateCSSRuleString.html (283722 => 283723)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/css/generateCSSRuleString.html       2021-10-07 18:08:53 UTC (rev 283722)
+++ trunk/LayoutTests/inspector/css/generateCSSRuleString.html  2021-10-07 18:15:14 UTC (rev 283723)
</span><span class="lines">@@ -1,81 +0,0 @@
</span><del>-<!DOCTYPE html>
-<html>
-<head>
-<style>
-*{a:1;b:2;c:3}
-body>*{a:1;b:2;c:3}
-@media all {body>div{a:1;b:2;c:3}}
-@media only screen and (min-width:0px) {body>#test-node{a:1;b:2;c:3}}
-@media only screen and (min-width:0px) {@media only screen and (min-height:0px) {body>div#test-node{a:1;b:2;c:3}}}
-</style>
-<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
-<script>
-function test() {
-    let nodeStyles = null;
-
-    let suite = InspectorTest.createSyncSuite("CSS.generateCSSRuleString");
-
-    suite.addTestCase({
-        name: "CSS.generateCSSRuleString.InlineStyle",
-        description: "Check the formatting of the generated inline style string.",
-        test() {
-            InspectorTest.log(nodeStyles.inlineStyle.generateCSSRuleString());
-        }
-    });
-
-    suite.addTestCase({
-        name: "CSS.generateCSSRuleString.InlineStyle.WithCommentedProperty",
-        description: "Check the formatting of the generated inline style string if a property is commented out.",
-        test() {
-            nodeStyles.inlineStyle.properties[1].commentOut(true);
-            InspectorTest.log(nodeStyles.inlineStyle.generateCSSRuleString());
-        },
-    });
-
-    suite.addTestCase({
-        name: "CSS.generateCSSRuleString.MatchedRules",
-        description: "Check the formatting of the generated string for all matched CSS rules.",
-        test() {
-            for (let rule of nodeStyles.matchedRules)
-                InspectorTest.log(rule.style.generateCSSRuleString());
-        }
-    });
-
-    suite.addTestCase({
-        name: "CSS.generateCSSRuleString.MatchedRules.WithCommentedProperties",
-        description: "Check the formatting of the generated string for all matched CSS rules if a property is commented out in each.",
-        test() {
-            for (let rule of nodeStyles.matchedRules) {
-                if (!rule.style.editable)
-                    continue;
-
-                rule.style.properties[1].commentOut(true);
-                InspectorTest.log(rule.style.generateCSSRuleString());
-            }
-        }
-    });
-
-    WI.domManager.requestDocument((documentNode) => {
-        documentNode.querySelector("#test-node", async (contentNodeId) => {
-            if (!contentNodeId) {
-                InspectorTest.log("DOM node not found.");
-                InspectorTest.completeTest();
-                return;
-            }
-
-            let domNode = WI.domManager.nodeForId(contentNodeId);
-            nodeStyles = WI.cssManager.stylesForNode(domNode);
-            if (nodeStyles.needsRefresh)
-                await nodeStyles.awaitEvent(WI.DOMNodeStyles.Event.Refreshed);
-
-            suite.runTestCasesAndFinish();
-        });
-    });
-}
-</script>
-</head>
-<body onload="runTest()">
-    <p>Testing that generated CSS rule strings have proper formatting.</p>
-    <div id="test-node" style="a:1;b:2;c:3"></div>
-</body>
-</html>
</del></span></pre></div>
<a id="trunkLayoutTestsinspectorcssgenerateFormattedTextexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/css/generateFormattedText-expected.txt (0 => 283723)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/css/generateFormattedText-expected.txt                               (rev 0)
+++ trunk/LayoutTests/inspector/css/generateFormattedText-expected.txt  2021-10-07 18:15:14 UTC (rev 283723)
</span><span class="lines">@@ -0,0 +1,192 @@
</span><ins>+Testing that generated CSS rule strings have proper formatting.
+
+
+== Running test suite: WI.CSSStyelDeclaration.prototype.generateFormattedText
+-- Running test case: CSS.generateFormattedText.InlineStyle
+a: 1; b: 2; c: 3;
+
+-- Running test case: CSS.generateFormattedText.InlineStyle.includeGroupingsAndSelectors
+#test-node {a: 1; b: 2; c: 3;}
+
+-- Running test case: CSS.generateFormattedText.InlineStyle.Multiline
+
+    a: 1;
+    b: 2;
+    c: 3;
+
+
+-- Running test case: CSS.generateFormattedText.InlineStyle.Multiline.includeGroupingsAndSelectors
+#test-node {
+    a: 1;
+    b: 2;
+    c: 3;
+}
+
+-- Running test case: CSS.generateFormattedText.InlineStyle.WithCommentedProperty
+a: 1; /* b: 2; */ c: 3;
+
+-- Running test case: CSS.generateFormattedText.InlineStyle.WithCommentedProperty.includeGroupingsAndSelectors
+#test-node {a: 1; /* b: 2; */ c: 3;}
+
+-- Running test case: CSS.generateFormattedText.InlineStyle.WithCommentedProperty.Multiline
+
+    a: 1;
+    /* b: 2; */
+    c: 3;
+
+
+-- Running test case: CSS.generateFormattedText.InlineStyle.WithCommentedProperty.includeGroupingsAndSelectors.Multiline
+#test-node {
+    a: 1;
+    /* b: 2; */
+    c: 3;
+}
+
+-- Running test case: CSS.generateFormattedText.MatchedRules
+a: 1; b: 2; c: 3;
+a: 1; b: 2; c: 3;
+a: 1; b: 2; c: 3;
+a: 1; b: 2; c: 3;
+a: 1; b: 2; c: 3;
+
+-- Running test case: CSS.generateFormattedText.MatchedRules.includeGroupingsAndSelectors
+@media only screen and (min-width: 0px) {@media only screen and (min-height: 0px) {body > div#test-node {a: 1; b: 2; c: 3;}}}
+@media only screen and (min-width: 0px) {body > #test-node {a: 1; b: 2; c: 3;}}
+body > div {a: 1; b: 2; c: 3;}
+body > * {a: 1; b: 2; c: 3;}
+* {a: 1; b: 2; c: 3;}
+
+-- Running test case: CSS.generateFormattedText.MatchedRules.Multiline
+
+            a: 1;
+            b: 2;
+            c: 3;
+
+
+        a: 1;
+        b: 2;
+        c: 3;
+
+
+    a: 1;
+    b: 2;
+    c: 3;
+
+
+    a: 1;
+    b: 2;
+    c: 3;
+
+
+    a: 1;
+    b: 2;
+    c: 3;
+
+
+-- Running test case: CSS.generateFormattedText.MatchedRules.includeGroupingsAndSelectors.Multiline
+@media only screen and (min-width: 0px) {
+    @media only screen and (min-height: 0px) {
+        body > div#test-node {
+            a: 1;
+            b: 2;
+            c: 3;
+        }
+    }
+}
+@media only screen and (min-width: 0px) {
+    body > #test-node {
+        a: 1;
+        b: 2;
+        c: 3;
+    }
+}
+body > div {
+    a: 1;
+    b: 2;
+    c: 3;
+}
+body > * {
+    a: 1;
+    b: 2;
+    c: 3;
+}
+* {
+    a: 1;
+    b: 2;
+    c: 3;
+}
+
+-- Running test case: CSS.generateFormattedText.MatchedRules.WithCommentedProperties
+a: 1; /* b: 2; */ c: 3;
+a: 1; /* b: 2; */ c: 3;
+a: 1; /* b: 2; */ c: 3;
+a: 1; /* b: 2; */ c: 3;
+a: 1; /* b: 2; */ c: 3;
+
+-- Running test case: CSS.generateFormattedText.MatchedRules.WithCommentedProperties.includeGroupingsAndSelectors
+@media only screen and (min-width: 0px) {@media only screen and (min-height: 0px) {body > div#test-node {a: 1; /* b: 2; */ c: 3;}}}
+@media only screen and (min-width: 0px) {body > #test-node {a: 1; /* b: 2; */ c: 3;}}
+body > div {a: 1; /* b: 2; */ c: 3;}
+body > * {a: 1; /* b: 2; */ c: 3;}
+* {a: 1; /* b: 2; */ c: 3;}
+
+-- Running test case: CSS.generateFormattedText.MatchedRules.WithCommentedProperties.Multiline
+
+            a: 1;
+            /* b: 2; */
+            c: 3;
+
+
+        a: 1;
+        /* b: 2; */
+        c: 3;
+
+
+    a: 1;
+    /* b: 2; */
+    c: 3;
+
+
+    a: 1;
+    /* b: 2; */
+    c: 3;
+
+
+    a: 1;
+    /* b: 2; */
+    c: 3;
+
+
+-- Running test case: CSS.generateFormattedText.MatchedRules.WithCommentedProperties.includeGroupingsAndSelectors.Multiline
+@media only screen and (min-width: 0px) {
+    @media only screen and (min-height: 0px) {
+        body > div#test-node {
+            a: 1;
+            /* b: 2; */
+            c: 3;
+        }
+    }
+}
+@media only screen and (min-width: 0px) {
+    body > #test-node {
+        a: 1;
+        /* b: 2; */
+        c: 3;
+    }
+}
+body > div {
+    a: 1;
+    /* b: 2; */
+    c: 3;
+}
+body > * {
+    a: 1;
+    /* b: 2; */
+    c: 3;
+}
+* {
+    a: 1;
+    /* b: 2; */
+    c: 3;
+}
+
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorcssgenerateFormattedTexthtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/css/generateFormattedText.html (0 => 283723)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/css/generateFormattedText.html                               (rev 0)
+++ trunk/LayoutTests/inspector/css/generateFormattedText.html  2021-10-07 18:15:14 UTC (rev 283723)
</span><span class="lines">@@ -0,0 +1,209 @@
</span><ins>+<!DOCTYPE html>
+<html>
+<head>
+<style>
+*{a:1;b:2;c:3}
+body>*{a:1;b:2;c:3}
+@media all {body>div{a:1;b:2;c:3}}
+@media only screen and (min-width:0px) {body>#test-node{a:1;b:2;c:3}}
+@media only screen and (min-width:0px) {@media only screen and (min-height:0px) {body>div#test-node{a:1;b:2;c:3}}}
+</style>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script>
+function test() {
+    let nodeStyles = null;
+
+    let suite = InspectorTest.createSyncSuite("WI.CSSStyelDeclaration.prototype.generateFormattedText");
+
+    suite.addTestCase({
+        name: "CSS.generateFormattedText.InlineStyle",
+        description: "Check the formatting of the generated inline style string.",
+        test() {
+            InspectorTest.log(nodeStyles.inlineStyle.generateFormattedText());
+        }
+    });
+
+    suite.addTestCase({
+        name: "CSS.generateFormattedText.InlineStyle.includeGroupingsAndSelectors",
+        description: "Check the formatting of the generated inline style string (including groupings and selectors).",
+        test() {
+            InspectorTest.log(nodeStyles.inlineStyle.generateFormattedText({includeGroupingsAndSelectors: true}));
+        }
+    });
+
+    suite.addTestCase({
+        name: "CSS.generateFormattedText.InlineStyle.Multiline",
+        description: "Check the formatting of the generated inline style string (multiline).",
+        test() {
+            InspectorTest.log(nodeStyles.inlineStyle.generateFormattedText({multiline: true}));
+        }
+    });
+
+    suite.addTestCase({
+        name: "CSS.generateFormattedText.InlineStyle.Multiline.includeGroupingsAndSelectors",
+        description: "Check the formatting of the generated inline style string (multiline, and including groupings and selectors).",
+        test() {
+            InspectorTest.log(nodeStyles.inlineStyle.generateFormattedText({multiline: true, includeGroupingsAndSelectors: true}));
+        }
+    });
+
+    suite.addTestCase({
+        name: "CSS.generateFormattedText.InlineStyle.WithCommentedProperty",
+        description: "Check the formatting of the generated inline style string if a property is commented out.",
+        test() {
+            nodeStyles.inlineStyle.properties[1].commentOut(true);
+            InspectorTest.log(nodeStyles.inlineStyle.generateFormattedText());
+        },
+    });
+
+    suite.addTestCase({
+        name: "CSS.generateFormattedText.InlineStyle.WithCommentedProperty.includeGroupingsAndSelectors",
+        description: "Check the formatting of the generated inline style string (including groupings and selectors) if a property is commented out.",
+        test() {
+            nodeStyles.inlineStyle.properties[1].commentOut(true);
+            InspectorTest.log(nodeStyles.inlineStyle.generateFormattedText({includeGroupingsAndSelectors: true}));
+        },
+    });
+
+    suite.addTestCase({
+        name: "CSS.generateFormattedText.InlineStyle.WithCommentedProperty.Multiline",
+        description: "Check the formatting of the generated inline style string (multiline) if a property is commented out.",
+        test() {
+            nodeStyles.inlineStyle.properties[1].commentOut(true);
+            InspectorTest.log(nodeStyles.inlineStyle.generateFormattedText({multiline: true}));
+        },
+    });
+
+    suite.addTestCase({
+        name: "CSS.generateFormattedText.InlineStyle.WithCommentedProperty.includeGroupingsAndSelectors.Multiline",
+        description: "Check the formatting of the generated inline style string (multiline, and including groupings and selectors) if a property is commented out.",
+        test() {
+            nodeStyles.inlineStyle.properties[1].commentOut(true);
+            InspectorTest.log(nodeStyles.inlineStyle.generateFormattedText({includeGroupingsAndSelectors: true, multiline: true}));
+        },
+    });
+
+    suite.addTestCase({
+        name: "CSS.generateFormattedText.MatchedRules",
+        description: "Check the formatting of the generated string for all matched CSS rules.",
+        test() {
+            for (let rule of nodeStyles.matchedRules) {
+                if (!rule.style.editable)
+                    continue;
+                InspectorTest.log(rule.style.generateFormattedText());
+            }
+        }
+    });
+
+    suite.addTestCase({
+        name: "CSS.generateFormattedText.MatchedRules.includeGroupingsAndSelectors",
+        description: "Check the formatting of the generated string (including groupings and selectors) for all matched CSS rules.",
+        test() {
+            for (let rule of nodeStyles.matchedRules) {
+                if (!rule.style.editable)
+                    continue;
+                InspectorTest.log(rule.style.generateFormattedText({includeGroupingsAndSelectors: true}));
+            }
+        }
+    });
+
+    suite.addTestCase({
+        name: "CSS.generateFormattedText.MatchedRules.Multiline",
+        description: "Check the formatting of the generated string (multiline) for all matched CSS rules.",
+        test() {
+            for (let rule of nodeStyles.matchedRules) {
+                if (!rule.style.editable)
+                    continue;
+                InspectorTest.log(rule.style.generateFormattedText({multiline: true}));
+            }
+        }
+    });
+
+    suite.addTestCase({
+        name: "CSS.generateFormattedText.MatchedRules.includeGroupingsAndSelectors.Multiline",
+        description: "Check the formatting of the generated string (multiline, and including groupings and selectors) for all matched CSS rules.",
+        test() {
+            for (let rule of nodeStyles.matchedRules) {
+                if (!rule.style.editable)
+                    continue;
+                InspectorTest.log(rule.style.generateFormattedText({includeGroupingsAndSelectors: true, multiline: true}));
+            }
+        }
+    });
+
+    suite.addTestCase({
+        name: "CSS.generateFormattedText.MatchedRules.WithCommentedProperties",
+        description: "Check the formatting of the generated string for all matched CSS rules if a property is commented out in each.",
+        test() {
+            for (let rule of nodeStyles.matchedRules) {
+                if (!rule.style.editable)
+                    continue;
+                rule.style.properties[1].commentOut(true);
+                InspectorTest.log(rule.style.generateFormattedText());
+            }
+        }
+    });
+
+    suite.addTestCase({
+        name: "CSS.generateFormattedText.MatchedRules.WithCommentedProperties.includeGroupingsAndSelectors",
+        description: "Check the formatting of the generated string (including groupings and selectors) for all matched CSS rules if a property is commented out in each.",
+        test() {
+            for (let rule of nodeStyles.matchedRules) {
+                if (!rule.style.editable)
+                    continue;
+                rule.style.properties[1].commentOut(true);
+                InspectorTest.log(rule.style.generateFormattedText({includeGroupingsAndSelectors: true}));
+            }
+        }
+    });
+
+    suite.addTestCase({
+        name: "CSS.generateFormattedText.MatchedRules.WithCommentedProperties.Multiline",
+        description: "Check the formatting of the generated string (multiline) for all matched CSS rules if a property is commented out in each.",
+        test() {
+            for (let rule of nodeStyles.matchedRules) {
+                if (!rule.style.editable)
+                    continue;
+                rule.style.properties[1].commentOut(true);
+                InspectorTest.log(rule.style.generateFormattedText({multiline: true}));
+            }
+        }
+    });
+
+    suite.addTestCase({
+        name: "CSS.generateFormattedText.MatchedRules.WithCommentedProperties.includeGroupingsAndSelectors.Multiline",
+        description: "Check the formatting of the generated string (multiline, and including groupings and selectors) for all matched CSS rules if a property is commented out in each.",
+        test() {
+            for (let rule of nodeStyles.matchedRules) {
+                if (!rule.style.editable)
+                    continue;
+                rule.style.properties[1].commentOut(true);
+                InspectorTest.log(rule.style.generateFormattedText({includeGroupingsAndSelectors: true, multiline: true}));
+            }
+        }
+    });
+
+    WI.domManager.requestDocument((documentNode) => {
+        documentNode.querySelector("#test-node", async (contentNodeId) => {
+            if (!contentNodeId) {
+                InspectorTest.log("DOM node not found.");
+                InspectorTest.completeTest();
+                return;
+            }
+
+            let domNode = WI.domManager.nodeForId(contentNodeId);
+            nodeStyles = WI.cssManager.stylesForNode(domNode);
+            if (nodeStyles.needsRefresh)
+                await nodeStyles.awaitEvent(WI.DOMNodeStyles.Event.Refreshed);
+
+            suite.runTestCasesAndFinish();
+        });
+    });
+}
+</script>
+</head>
+<body onload="runTest()">
+    <p>Testing that generated CSS rule strings have proper formatting.</p>
+    <div id="test-node" style="a:1;b:2;c:3"></div>
+</body>
+</html>
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorcssmodifycsspropertyexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/css/modify-css-property-expected.txt (283722 => 283723)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/css/modify-css-property-expected.txt 2021-10-07 18:08:53 UTC (rev 283722)
+++ trunk/LayoutTests/inspector/css/modify-css-property-expected.txt    2021-10-07 18:15:14 UTC (rev 283723)
</span><span class="lines">@@ -5,6 +5,7 @@
</span><span class="cx"> -- Running test case: Update value when CSSStyleDeclaration is not locked
</span><span class="cx"> PASS: "font-size" property value should update immediately.
</span><span class="cx"> PASS: Style declaration text should update immediately.
</span><ins>+PASS: styleSheetTextRange should correspond to "font-size: 10px;"
</ins><span class="cx"> 
</span><span class="cx"> -- Running test case: Update value when CSSStyleDeclaration is locked
</span><span class="cx"> PASS: "font-size" property value should update immediately.
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorcssmodifycsspropertyhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/css/modify-css-property.html (283722 => 283723)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/css/modify-css-property.html 2021-10-07 18:08:53 UTC (rev 283722)
+++ trunk/LayoutTests/inspector/css/modify-css-property.html    2021-10-07 18:15:14 UTC (rev 283723)
</span><span class="lines">@@ -1,6 +1,7 @@
</span><span class="cx"> <!DOCTYPE html>
</span><span class="cx"> <html>
</span><span class="cx"> <head>
</span><ins>+<link rel="stylesheet" href="resources/modify-css-property.css">
</ins><span class="cx"> <script src="../../http/tests/inspector/resources/inspector-test.js"></script>
</span><span class="cx"> <script>
</span><span class="cx"> function makeNarrow() {
</span><span class="lines">@@ -39,14 +40,22 @@
</span><span class="cx"> 
</span><span class="cx">             let styleDeclaration = getMatchedStyleDeclaration();
</span><span class="cx">             styleDeclaration.locked = false;
</span><ins>+
+            styleDeclaration.singleFireEventListener(WI.CSSStyleDeclaration.Event.PropertiesChanged, function(event) {
+                let fontProperty = getProperty("font-size");
+                let relativeRange = fontProperty.styleSheetTextRange.relativeTo(fontProperty.ownerStyle.styleSheetTextRange.startLine, fontProperty.ownerStyle.styleSheetTextRange.startOffset);
+                relativeRange.resolveOffsets(fontProperty.ownerStyle.text);
+                let propertyText = fontProperty.ownerStyle.text.slice(relativeRange.startOffset, relativeRange.endOffset);
+                InspectorTest.expectEqual(propertyText, "font-size: 10px;", `styleSheetTextRange should correspond to "font-size: 10px;"`);
+                resolve();
+            });
+
</ins><span class="cx">             getProperty("font-size").rawValue = "11px";
</span><span class="cx">             getProperty("font-size").rawValue = "10px";
</span><span class="cx"> 
</span><span class="cx">             InspectorTest.expectEqual(getProperty("font-size").rawValue, "10px", `"font-size" property value should update immediately.`);
</span><span class="cx"> 
</span><del>-            InspectorTest.expectEqual(styleDeclaration.text, `font-size: 10px; color: antiquewhite`, `Style declaration text should update immediately.`);
-
-            resolve();
</del><ins>+            InspectorTest.expectEqual(styleDeclaration.text, `\n    font-size: 10px;\n    color: antiquewhite;\n`, `Style declaration text should update immediately.`);
</ins><span class="cx">         }
</span><span class="cx">     });
</span><span class="cx"> 
</span><span class="lines">@@ -80,13 +89,12 @@
</span><span class="cx">             InspectorTest.expectEqual(getProperty("font-size").rawValue, "16px", `"font-size" property value should update immediately.`);
</span><span class="cx"> 
</span><span class="cx">             InspectorTest.expectEqual(styleDeclaration.text, `
</span><del>-        font-size: 16px;
-        color: #333;
</del><ins>+    font-size: 16px;
+    color: #333;
+    margin-left: 0;
+    margin-top: 1em;
+`, `Style declaration text should update immediately.`);
</ins><span class="cx"> 
</span><del>-        margin-left: 0;
-        margin-top: 1em;
-    `, `Style declaration text should update immediately.`);
-
</del><span class="cx">             styleDeclaration.locked = false;
</span><span class="cx"> 
</span><span class="cx">             resolve();
</span><span class="lines">@@ -243,7 +251,7 @@
</span><span class="cx">             let disabled = false;
</span><span class="cx">             getProperty("padding-right").commentOut(disabled);
</span><span class="cx"> 
</span><del>-            let expectedStyleText = `\n        /* padding-left: 2em; */\n        padding-right: 0px;\n    `;
</del><ins>+            let expectedStyleText = `\n    /* padding-left: 2em; */\n    padding-right: 0px;\n`;
</ins><span class="cx">             InspectorTest.expectEqual(styleDeclaration.text, expectedStyleText, `Style declaration text should update immediately with uncommented property.`);
</span><span class="cx"> 
</span><span class="cx">             InspectorTest.expectThat(getProperty("padding-right").enabled, `Uncommented property should be enabled.`);
</span><span class="lines">@@ -251,7 +259,7 @@
</span><span class="cx">             disabled = true;
</span><span class="cx">             getProperty("padding-right").commentOut(disabled);
</span><span class="cx"> 
</span><del>-            expectedStyleText = `\n        /* padding-left: 2em; */\n        /* padding-right: 0px; */\n    `;
</del><ins>+            expectedStyleText = `\n    /* padding-left: 2em; */\n    /* padding-right: 0px; */\n`;
</ins><span class="cx">             InspectorTest.expectEqual(styleDeclaration.text, expectedStyleText, `Style declaration text should update immediately with commented out property.`);
</span><span class="cx"> 
</span><span class="cx">             InspectorTest.expectThat(!getProperty("padding-right").enabled, `Commented out property should be disabled.`);
</span><span class="lines">@@ -290,7 +298,7 @@
</span><span class="cx">             let disabled = false;
</span><span class="cx">             getProperty("font-size").commentOut(disabled);
</span><span class="cx"> 
</span><del>-            let expectedStyleText = `font-size: 13px;/*border: 2px solid brown*/`;
</del><ins>+            let expectedStyleText = `\n    font-size: 13px;\n    /* border: 2px solid brown; */\n`;
</ins><span class="cx">             InspectorTest.expectEqual(styleDeclaration.text, expectedStyleText, `Style declaration text should update immediately with uncommented property.`);
</span><span class="cx"> 
</span><span class="cx">             InspectorTest.expectThat(getProperty("font-size").enabled, `Uncommented property should be enabled.`);
</span><span class="lines">@@ -299,7 +307,7 @@
</span><span class="cx">             disabled = false;
</span><span class="cx">             getProperty("border").commentOut(disabled);
</span><span class="cx"> 
</span><del>-            expectedStyleText = `font-size: 13px;border: 2px solid brown`;
</del><ins>+            expectedStyleText = `\n    font-size: 13px;\n    border: 2px solid brown\n`;
</ins><span class="cx">             InspectorTest.expectEqual(styleDeclaration.text, expectedStyleText, `Style declaration text should update immediately with commented out property.`);
</span><span class="cx"> 
</span><span class="cx">             InspectorTest.expectThat(getProperty("border").enabled, `Uncommented property should be enabled.`);
</span><span class="lines">@@ -331,22 +339,6 @@
</span><span class="cx"> </head>
</span><span class="cx"> <body onload="runTest()">
</span><span class="cx">     <p>Testing that CSSStyleDeclaration update immediately after modifying its properties when it is not locked.</p>
</span><del>-
-    <style>
-    .rule-a {
-        font-size: 14px;
-        color: #333;
-
-        margin-left: 0;
-        margin-top: 1em;
-    }
-    .rule-b {font-size: 12px; color: antiquewhite}
-    .rule-c {
-        /* padding-left: 2em; */
-        /* padding-right: 0px; */
-    }
-    .rule-d {/*font-size: 13px;*//*border: 2px solid brown*/}
-    </style>
</del><span class="cx">     <div id="x" class="test-node rule-a rule-b rule-c rule-d" style="width: 100px"></div>
</span><span class="cx"> </body>
</span><span class="cx"> </html>
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorcssmodifyinlinestyleexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/css/modify-inline-style-expected.txt (283722 => 283723)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/css/modify-inline-style-expected.txt 2021-10-07 18:08:53 UTC (rev 283722)
+++ trunk/LayoutTests/inspector/css/modify-inline-style-expected.txt    2021-10-07 18:15:14 UTC (rev 283723)
</span><span class="lines">@@ -7,12 +7,12 @@
</span><span class="cx"> font: 12px normal sans-serif!important;
</span><span class="cx"> 
</span><span class="cx"> CSSProperty changed to "color: red;".
</span><del>-font: 12px normal sans-serif!important;color: red;
</del><ins>+font: 12px normal sans-serif!important; color: red;
</ins><span class="cx"> 
</span><span class="cx"> CSSProperty changed to "font: 12px sans-serif;".
</span><del>-font: 12px sans-serif;color: red;
</del><ins>+font: 12px sans-serif; color: red;
</ins><span class="cx"> 
</span><span class="cx"> CSSProperty changed to "color: invalid_c010r;".
</span><del>-font: 12px sans-serif;color: invalid_c010r;
</del><ins>+font: 12px sans-serif; color: invalid_c010r;
</ins><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorcssresourcesmodifycsspropertycss"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/css/resources/modify-css-property.css (0 => 283723)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/css/resources/modify-css-property.css                                (rev 0)
+++ trunk/LayoutTests/inspector/css/resources/modify-css-property.css   2021-10-07 18:15:14 UTC (rev 283723)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+/* A comment. */
+
+.rule-a {
+    font-size: 14px;
+    color: #333;
+
+    margin-left: 0;
+    margin-top: 1em;
+}
+.rule-b {font-size: 12px; color: antiquewhite}
+.rule-c {
+    /* padding-left: 2em; */
+    /* padding-right: 0px; */
+}
+.rule-d {/*font-size: 13px;*//*border: 2px solid brown*/}
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/ChangeLog (283722 => 283723)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/ChangeLog    2021-10-07 18:08:53 UTC (rev 283722)
+++ trunk/Source/WebInspectorUI/ChangeLog       2021-10-07 18:15:14 UTC (rev 283723)
</span><span class="lines">@@ -1,3 +1,44 @@
</span><ins>+2021-10-07  Nikita Vasilyev  <nvasilyev@apple.com>
+
+        Web Inspector: Styles: format style declarations after editing
+        https://bugs.webkit.org/show_bug.cgi?id=178835
+        <rdar://problem/35185060>
+
+        Reviewed by Devin Rousso.
+
+        Indent CSS properties with spaces/tabs set in Web Inspector settings. Increse indentation level when CSS rules are
+        inside of at-rules (e.g. @media, @keyframes, @supports).
+
+        Don't indent CSS properties in style attributes. Keep them on the single line, separated by a space character:
+
+            style="font-size: 12px; color: black;"
+
+        * UserInterface/Models/CSSProperty.js:
+        (WI.CSSProperty.prototype.set text):
+        Introduce `_isTextPendingSave` flag. It's needed when saving pasted text, and saving commented out or uncommented CSS properties.
+
+        (WI.CSSProperty.prototype.get formattedText):
+
+        (WI.CSSProperty.prototype.replaceWithText): Deleted.
+        This is redundant - setting `text` works the same.
+
+        (WI.CSSProperty.prototype._updateStyleText):
+        (WI.CSSProperty.prototype._updateOwnerStyleText):
+        (WI.CSSProperty.prototype._prependSemicolonIfNeeded): Deleted.
+        Greatly simplify the logic now that we save formatted text and don't modify styleText.
+
+        * UserInterface/Models/CSSStyleDeclaration.js:
+        (WI.CSSStyleDeclaration.prototype.removeProperty):
+        Remode unnecessary code that modifies `_styleSheetTextRange`. The backend sends new `_styleSheetTextRange` data
+        upon a change.
+
+        (WI.CSSStyleDeclaration.prototype.generateFormattedText): Renamed from 'generateCSSRuleString'.
+
+        * UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js:
+        (WI.SpreadsheetCSSStyleDeclarationSection.prototype._populateIconElementContextMenu):
+        * UserInterface/Views/SpreadsheetStyleProperty.js:
+        (WI.SpreadsheetStyleProperty.prototype.remove):
+
</ins><span class="cx"> 2021-10-05  Patrick Angle  <pangle@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Web Inspector: Show color space for canvases in the Graphics tab on the overview cards
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsCSSPropertyjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js (283722 => 283723)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js  2021-10-07 18:08:53 UTC (rev 283722)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js     2021-10-07 18:15:14 UTC (rev 283723)
</span><span class="lines">@@ -34,6 +34,7 @@
</span><span class="cx">         this._overridingProperty = null;
</span><span class="cx">         this._initialState = null;
</span><span class="cx">         this._modified = false;
</span><ins>+        this._isUpdatingText = false;
</ins><span class="cx"> 
</span><span class="cx">         this.update(text, name, value, priority, enabled, overridden, implicit, anonymous, valid, styleSheetTextRange, true);
</span><span class="cx">     }
</span><span class="lines">@@ -184,13 +185,6 @@
</span><span class="cx">         this._updateStyleText(forceRemove);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    replaceWithText(text)
-    {
-        this._markModified();
-
-        this._updateOwnerStyleText(this._text, text, true);
-    }
-
</del><span class="cx">     commentOut(disabled)
</span><span class="cx">     {
</span><span class="cx">         console.assert(this.editable);
</span><span class="lines">@@ -213,16 +207,22 @@
</span><span class="cx"> 
</span><span class="cx">     set text(newText)
</span><span class="cx">     {
</span><ins>+        newText = newText.trim();
</ins><span class="cx">         if (this._text === newText)
</span><span class="cx">             return;
</span><span class="cx"> 
</span><span class="cx">         this._markModified();
</span><del>-        this._updateOwnerStyleText(this._text, newText);
</del><span class="cx">         this._text = newText;
</span><ins>+        this._isUpdatingText = true;
+        this._updateOwnerStyleText();
+        this._isUpdatingText = false;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     get formattedText()
</span><span class="cx">     {
</span><ins>+        if (this._isUpdatingText)
+            return this._text;
+
</ins><span class="cx">         if (!this._name)
</span><span class="cx">             return "";
</span><span class="cx"> 
</span><span class="lines">@@ -501,74 +501,24 @@
</span><span class="cx">             text = this._name + ": " + value + ";";
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        let oldText = this._text;
</del><span class="cx">         this._text = text;
</span><del>-        this._updateOwnerStyleText(oldText, this._text, forceRemove);
</del><ins>+
+        if (forceRemove)
+            this._ownerStyle.removeProperty(this);
+
+        this._updateOwnerStyleText();
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    _updateOwnerStyleText(oldText, newText, forceRemove = false)
</del><ins>+    _updateOwnerStyleText()
</ins><span class="cx">     {
</span><del>-        if (oldText === newText) {
-            if (forceRemove) {
-                const lineDelta = 0;
-                const columnDelta = 0;
-                this._ownerStyle.shiftPropertiesAfter(this, lineDelta, columnDelta, forceRemove);
-                this._ownerStyle.updatePropertiesModifiedState();
-            }
-            return;
-        }
-
</del><span class="cx">         console.assert(this._ownerStyle);
</span><span class="cx">         if (!this._ownerStyle)
</span><span class="cx">             return;
</span><span class="cx"> 
</span><del>-        this._prependSemicolonIfNeeded();
-
-        let styleText = this._ownerStyle.text || "";
-
-        // _styleSheetTextRange is the position of the property within the stylesheet.
-        // range is the position of the property within the rule.
-        let range = this._styleSheetTextRange.relativeTo(this._ownerStyle.styleSheetTextRange.startLine, this._ownerStyle.styleSheetTextRange.startColumn);
-
-        // Append a line break to count the last line of styleText towards endOffset.
-        range.resolveOffsets(styleText + "\n");
-
-        console.assert(oldText === styleText.slice(range.startOffset, range.endOffset), "_styleSheetTextRange data is invalid.");
-
-        if (WI.settings.debugEnableStyleEditingDebugMode.value) {
-            let prefix = styleText.slice(0, range.startOffset);
-            let postfix = styleText.slice(range.endOffset);
-            console.info(`${prefix}%c${oldText}%c${newText}%c${postfix}`, `background: hsl(356, 100%, 90%); color: black`, `background: hsl(100, 100%, 91%); color: black`, `background: transparent`);
-        }
-
-        let newStyleText = styleText.slice(0, range.startOffset) + newText + styleText.slice(range.endOffset);
-
-        let lineDelta = newText.lineCount - oldText.lineCount;
-        let columnDelta = newText.lastLine.length - oldText.lastLine.length;
-        this._styleSheetTextRange = this._styleSheetTextRange.cloneAndModify(0, 0, lineDelta, columnDelta);
-
-        this._ownerStyle.text = newStyleText;
-
-        let propertyWasRemoved = !newText;
-        this._ownerStyle.shiftPropertiesAfter(this, lineDelta, columnDelta, propertyWasRemoved);
</del><ins>+        this._ownerStyle.text = this._ownerStyle.generateFormattedText({multiline: this._ownerStyle.type === WI.CSSStyleDeclaration.Type.Rule});
</ins><span class="cx">         this._ownerStyle.updatePropertiesModifiedState();
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    _prependSemicolonIfNeeded()
-    {
-        for (let i = this.index - 1; i >= 0; --i) {
-            let property = this._ownerStyle.properties[i];
-            if (!property.enabled)
-                continue;
-
-            let match = property.text.match(/[^;\s](\s*)$/);
-            if (match)
-                property.text = property.text.trimRight() + ";" + match[1];
-
-            break;
-        }
-    }
-
</del><span class="cx">     _markModified()
</span><span class="cx">     {
</span><span class="cx">         if (this._ownerStyle)
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsCSSStyleDeclarationjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Models/CSSStyleDeclaration.js (283722 => 283723)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/CSSStyleDeclaration.js  2021-10-07 18:08:53 UTC (rev 283722)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/CSSStyleDeclaration.js     2021-10-07 18:15:14 UTC (rev 283723)
</span><span class="lines">@@ -445,7 +445,7 @@
</span><span class="cx">         WI.cssManager.addModifiedStyle(this);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    shiftPropertiesAfter(cssProperty, lineDelta, columnDelta, propertyWasRemoved)
</del><ins>+    removeProperty(cssProperty)
</ins><span class="cx">     {
</span><span class="cx">         // cssProperty.index could be set to NaN by WI.CSSStyleDeclaration.prototype.update.
</span><span class="cx">         let realIndex = this._properties.indexOf(cssProperty);
</span><span class="lines">@@ -452,26 +452,8 @@
</span><span class="cx">         if (realIndex === -1)
</span><span class="cx">             return;
</span><span class="cx"> 
</span><del>-        let endLine = cssProperty.styleSheetTextRange.endLine;
</del><ins>+        this._properties.splice(realIndex, 1);
</ins><span class="cx"> 
</span><del>-        for (let i = realIndex + 1; i < this._properties.length; i++) {
-            let property = this._properties[i];
-
-            if (property._styleSheetTextRange) {
-                if (property.styleSheetTextRange.startLine === endLine) {
-                    // Only update column data if it's on the same line.
-                    property._styleSheetTextRange = property._styleSheetTextRange.cloneAndModify(lineDelta, columnDelta, lineDelta, columnDelta);
-                } else
-                    property._styleSheetTextRange = property._styleSheetTextRange.cloneAndModify(lineDelta, 0, lineDelta, 0);
-            }
-
-            if (propertyWasRemoved && !isNaN(property._index))
-                property._index--;
-        }
-
-        if (propertyWasRemoved)
-            this._properties.splice(realIndex, 1);
-
</del><span class="cx">         // Invalidate cached properties.
</span><span class="cx">         this._enabledProperties = null;
</span><span class="cx">         this._visibleProperties = null;
</span><span class="lines">@@ -507,25 +489,60 @@
</span><span class="cx">             WI.cssManager.removeModifiedStyle(this);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    generateCSSRuleString()
</del><ins>+    generateFormattedText(options = {})
</ins><span class="cx">     {
</span><span class="cx">         let indentString = WI.indentString();
</span><span class="cx">         let styleText = "";
</span><span class="cx">         let groupings = this.groupings.filter((grouping) => grouping.text !== "all");
</span><span class="cx">         let groupingsCount = groupings.length;
</span><del>-        for (let i = groupingsCount - 1; i >= 0; --i)
-            styleText += indentString.repeat(groupingsCount - i - 1) + groupings[i].prefix + " " + groupings[i].text + " {\n";
</del><span class="cx"> 
</span><del>-        styleText += indentString.repeat(groupingsCount) + this.selectorText + " {\n";
</del><ins>+        if (options.includeGroupingsAndSelectors) {
+            for (let i = groupingsCount - 1; i >= 0; --i) {
+                if (options.multiline)
+                    styleText += indentString.repeat(groupingsCount - i - 1);
</ins><span class="cx"> 
</span><del>-        for (let property of (this._styleSheetTextRange ? this.visibleProperties : this._properties))
-            styleText += indentString.repeat(groupingsCount + 1) + property.formattedText + "\n";
</del><ins>+                styleText += groupings[i].prefix + " " + groupings[i].text + " {";
</ins><span class="cx"> 
</span><del>-        for (let i = groupingsCount; i > 0; --i)
-            styleText += indentString.repeat(i) + "}\n";
</del><ins>+                if (options.multiline)
+                    styleText += "\n";
+            }
</ins><span class="cx"> 
</span><del>-        styleText += "}";
</del><ins>+            if (options.multiline)
+                styleText += indentString.repeat(groupingsCount);
</ins><span class="cx"> 
</span><ins>+            styleText += this.selectorText + " {";
+        }
+
+        let properties = this._styleSheetTextRange ? this.visibleProperties : this._properties;
+        if (properties.length) {
+            if (options.multiline) {
+                let propertyIndent = indentString.repeat(groupingsCount + 1);
+                for (let property of properties)
+                    styleText += "\n" + propertyIndent + property.formattedText;
+
+                styleText += "\n";
+                if (!options.includeGroupingsAndSelectors) {
+                    // Indent the closing "}" for nested rules.
+                    styleText += indentString.repeat(groupingsCount);
+                }
+            } else
+                styleText += properties.map((property) => property.formattedText).join(" ");
+        }
+
+        if (options.includeGroupingsAndSelectors) {
+            for (let i = groupingsCount; i > 0; --i) {
+                if (options.multiline)
+                    styleText += indentString.repeat(i);
+
+                styleText += "}";
+
+                if (options.multiline)
+                    styleText += "\n";
+            }
+
+            styleText += "}";
+        }
+
</ins><span class="cx">         return styleText;
</span><span class="cx">     }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetCSSStyleDeclarationSectionjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js (283722 => 283723)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js 2021-10-07 18:08:53 UTC (rev 283722)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js    2021-10-07 18:15:14 UTC (rev 283723)
</span><span class="lines">@@ -481,7 +481,7 @@
</span><span class="cx">     _populateIconElementContextMenu(contextMenu)
</span><span class="cx">     {
</span><span class="cx">         contextMenu.appendItem(WI.UIString("Copy Rule"), () => {
</span><del>-            InspectorFrontendHost.copyText(this._style.generateCSSRuleString());
</del><ins>+            InspectorFrontendHost.copyText(this._style.generateFormattedText({includeGroupingsAndSelectors: true, multiline: true}));
</ins><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         if (this._style.editable && this._style.properties.length) {
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetStylePropertyjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js (283722 => 283723)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js      2021-10-07 18:08:53 UTC (rev 283722)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js 2021-10-07 18:15:14 UTC (rev 283723)
</span><span class="lines">@@ -144,7 +144,7 @@
</span><span class="cx">         this.element.remove();
</span><span class="cx"> 
</span><span class="cx">         if (replacement)
</span><del>-            this._property.replaceWithText(replacement);
</del><ins>+            this._property.text = replacement;
</ins><span class="cx">         else
</span><span class="cx">             this._property.remove();
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>