Title: [261772] trunk
Revision
261772
Author
drou...@apple.com
Date
2020-05-15 18:17:21 -0700 (Fri, 15 May 2020)

Log Message

Web Inspector: Fails to pretty-print a particular CSS file
https://bugs.webkit.org/show_bug.cgi?id=211930

Reviewed by Joseph Pecoraro.

Source/WebInspectorUI:

* UserInterface/Workers/Formatter/CSSFormatter.js:
(CSSFormatter.prototype._format):
Keep a stack of special sequences (e.g. `"`, `'`, `/*`, etc.), only outputting the text in
between the start and end of the sequence when the stack is empty. Ignore all other special
sequences when in a comma sequence. Add proper checks for if the star/end is escaped.
Drive-by: minor refactor so that the arrow functions are created outside the loop.

LayoutTests:

* inspector/formatting/formatting-css.html:
* inspector/formatting/formatting-css-expected.txt:
* inspector/formatting/resources/css-tests/url.css: Added.
* inspector/formatting/resources/css-tests/url-expected.css: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (261771 => 261772)


--- trunk/LayoutTests/ChangeLog	2020-05-16 00:43:54 UTC (rev 261771)
+++ trunk/LayoutTests/ChangeLog	2020-05-16 01:17:21 UTC (rev 261772)
@@ -1,3 +1,15 @@
+2020-05-15  Devin Rousso  <drou...@apple.com>
+
+        Web Inspector: Fails to pretty-print a particular CSS file
+        https://bugs.webkit.org/show_bug.cgi?id=211930
+
+        Reviewed by Joseph Pecoraro.
+
+        * inspector/formatting/formatting-css.html:
+        * inspector/formatting/formatting-css-expected.txt:
+        * inspector/formatting/resources/css-tests/url.css: Added.
+        * inspector/formatting/resources/css-tests/url-expected.css: Added.
+
 2020-05-15  Ryan Haddad  <ryanhad...@apple.com>
 
         media/video-poster-set-after-playback.html is a flaky failure

Modified: trunk/LayoutTests/inspector/formatting/formatting-css-expected.txt (261771 => 261772)


--- trunk/LayoutTests/inspector/formatting/formatting-css-expected.txt	2020-05-16 00:43:54 UTC (rev 261771)
+++ trunk/LayoutTests/inspector/formatting/formatting-css-expected.txt	2020-05-16 01:17:21 UTC (rev 261772)
@@ -9,5 +9,6 @@
 PASS: keyframes.css
 PASS: media-query.css
 PASS: selectors.css
+PASS: url.css
 PASS: wrapping.css
 

Modified: trunk/LayoutTests/inspector/formatting/formatting-css.html (261771 => 261772)


--- trunk/LayoutTests/inspector/formatting/formatting-css.html	2020-05-16 00:43:54 UTC (rev 261771)
+++ trunk/LayoutTests/inspector/formatting/formatting-css.html	2020-05-16 01:17:21 UTC (rev 261772)
@@ -15,6 +15,7 @@
         "resources/css-tests/keyframes.css",
         "resources/css-tests/media-query.css",
         "resources/css-tests/selectors.css",
+        "resources/css-tests/url.css",
         "resources/css-tests/wrapping.css",
     ]);
 

Added: trunk/LayoutTests/inspector/formatting/resources/css-tests/url-expected.css (0 => 261772)


--- trunk/LayoutTests/inspector/formatting/resources/css-tests/url-expected.css	                        (rev 0)
+++ trunk/LayoutTests/inspector/formatting/resources/css-tests/url-expected.css	2020-05-16 01:17:21 UTC (rev 261772)
@@ -0,0 +1,27 @@
+body {
+    background: url(basic);
+    background: url(with space);
+    background: url('  single  ');
+    background: url("  double  ");
+    background: url('  nested  "  double  "  single  ');
+    background: url("  nested  '  single  '  double  ");
+    background: url('  escaped  \'  single  \'  single  ');
+    background: url("  escaped  \"  double  \"  double  ");
+    background: url('  escaped  \\\'  single  \\\'  single  ');
+    background: url("  escaped  \\\"  double  \\\"  double  ");
+    background: url('  nested  /*  comment  */  single  ');
+    background: url("  nested  /*  comment  */  dobule  ");
+    background: url( /*  before  */ '  single  ' /*  after  */);
+    background: url( /*  before  */ "  double  " /*  after  */);
+}
+
+body {
+    background-size: cover;
+    background-repeat: no-repeat;
+    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg clip-rule='evenodd' fill-rule='evenodd' stroke-linejoin='round' stroke-miterlimit='1.5' viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3CclipPath id='a'%3E%3Cpath d='M0 0h200v200H0z'/%3E%3C/clipPath%3E%3Cg clip-path='url(%23a)' transform='scale(0.5)'%3E%3Cimage height='200' transform='scale(2 2)' width='200' xlink:href=''/%3E%3C/g%3E%3C/svg%3E")
+}
+
+body {
+    background-image: linear-gradient(180deg, white, rgba(0, 0, 0, 0) 200px);
+    min-height: 200px
+}

Added: trunk/LayoutTests/inspector/formatting/resources/css-tests/url.css (0 => 261772)


--- trunk/LayoutTests/inspector/formatting/resources/css-tests/url.css	                        (rev 0)
+++ trunk/LayoutTests/inspector/formatting/resources/css-tests/url.css	2020-05-16 01:17:21 UTC (rev 261772)
@@ -0,0 +1,24 @@
+body {
+    background  :  url  (  basic  )  ;
+    background  :  url  (  with space  )  ;
+
+    background  :  url  (  '  single  '  )  ;
+    background  :  url  (  "  double  "  )  ;
+
+    background  :  url  (  '  nested  "  double  "  single  '  )  ;
+    background  :  url  (  "  nested  '  single  '  double  "  )  ;
+
+    background  :  url  (  '  escaped  \'  single  \'  single  '  )  ;
+    background  :  url  (  "  escaped  \"  double  \"  double  "  )  ;
+
+    background  :  url  (  '  escaped  \\\'  single  \\\'  single  '  )  ;
+    background  :  url  (  "  escaped  \\\"  double  \\\"  double  "  )  ;
+
+    background  :  url  (  '  nested  /*  comment  */  single  '  )  ;
+    background  :  url  (  "  nested  /*  comment  */  dobule  "  )  ;
+
+    background  :  url  (  /*  before  */  '  single  '  /*  after  */  )  ;
+    background  :  url  (  /*  before  */  "  double  "  /*  after  */  )  ;
+}
+
+body{background-size:cover;background-repeat:no-repeat;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg clip-rule='evenodd' fill-rule='evenodd' stroke-linejoin='round' stroke-miterlimit='1.5' viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3CclipPath id='a'%3E%3Cpath d='M0 0h200v200H0z'/%3E%3C/clipPath%3E%3Cg clip-path='url(%23a)' transform='scale(0.5)'%3E%3Cimage height='200' transform='scale(2 2)' width='200' xlink:href=''/%3E%3C/g%3E%3C/svg%3E")}body{background-image:linear-gradient(180deg,white,rgba(0,0,0,0) 200px);min-height:200px}

Modified: trunk/Source/WebInspectorUI/ChangeLog (261771 => 261772)


--- trunk/Source/WebInspectorUI/ChangeLog	2020-05-16 00:43:54 UTC (rev 261771)
+++ trunk/Source/WebInspectorUI/ChangeLog	2020-05-16 01:17:21 UTC (rev 261772)
@@ -1,3 +1,17 @@
+2020-05-15  Devin Rousso  <drou...@apple.com>
+
+        Web Inspector: Fails to pretty-print a particular CSS file
+        https://bugs.webkit.org/show_bug.cgi?id=211930
+
+        Reviewed by Joseph Pecoraro.
+
+        * UserInterface/Workers/Formatter/CSSFormatter.js:
+        (CSSFormatter.prototype._format):
+        Keep a stack of special sequences (e.g. `"`, `'`, `/*`, etc.), only outputting the text in
+        between the start and end of the sequence when the stack is empty. Ignore all other special
+        sequences when in a comma sequence. Add proper checks for if the star/end is escaped.
+        Drive-by: minor refactor so that the arrow functions are created outside the loop.
+
 2020-05-13  Devin Rousso  <drou...@apple.com>
 
         Web Inspector: rename CSS.StyleSheetOrigin.Regular to CSS.StyleSheetOrigin.Author to match the spec

Modified: trunk/Source/WebInspectorUI/UserInterface/Workers/Formatter/CSSFormatter.js (261771 => 261772)


--- trunk/Source/WebInspectorUI/UserInterface/Workers/Formatter/CSSFormatter.js	2020-05-16 00:43:54 UTC (rev 261771)
+++ trunk/Source/WebInspectorUI/UserInterface/Workers/Formatter/CSSFormatter.js	2020-05-16 01:17:21 UTC (rev 261772)
@@ -91,167 +91,184 @@
         const lastTokenWasOpenParenthesisRegExp = /\(\s*$/;
 
         let depth = 0;
+        let specialSequenceStack = [];
 
-        for (let i = 0; i < this._sourceText.length; ++i) {
-            let c = this._sourceText[i];
+        let index = 0;
+        let current = null;
 
-            let testCurrentLine = (regExp) => regExp.test(this._builder.currentLine);
+        let testCurrentLine = (regExp) => regExp.test(this._builder.currentLine);
 
-            let inComment = false;
+        let inSelector = () => {
+            let nextOpenBrace = this._sourceText.indexOf(`{`, index);
+            if (nextOpenBrace !== -1) {
+                let nextQuote = Infinity;
+                for (let quoteType of quoteTypes) {
+                    let quoteIndex = this._sourceText.indexOf(quoteType, index);
+                    if (quoteIndex !== -1 && quoteIndex < nextQuote)
+                        nextQuote = quoteIndex;
+                }
+                if (nextOpenBrace < nextQuote) {
+                    let nextSemicolon = this._sourceText.indexOf(`;`, index);
+                    if (nextSemicolon === -1)
+                        nextSemicolon = Infinity;
 
-            let inSelector = () => {
-                let nextOpenBrace = this._sourceText.indexOf(`{`, i);
-                if (nextOpenBrace !== -1) {
-                    let nextQuote = Infinity;
-                    for (let quoteType of quoteTypes) {
-                        let quoteIndex = this._sourceText.indexOf(quoteType, i);
-                        if (quoteIndex !== -1 && quoteIndex < nextQuote)
-                            nextQuote = quoteIndex;
-                    }
-                    if (nextOpenBrace < nextQuote) {
-                        let nextSemicolon = this._sourceText.indexOf(`;`, i);
-                        if (nextSemicolon === -1)
-                            nextSemicolon = Infinity;
+                    let nextNewline = this._sourceText.indexOf(`\n`, index);
+                    if (nextNewline === -1)
+                        nextNewline = Infinity;
 
-                        let nextNewline = this._sourceText.indexOf(`\n`, i);
-                        if (nextNewline === -1)
-                            nextNewline = Infinity;
-
-                        if (nextOpenBrace < Math.min(nextSemicolon, nextNewline))
-                            return true;
-                    }
+                    if (nextOpenBrace < Math.min(nextSemicolon, nextNewline))
+                        return true;
                 }
+            }
 
-                if (testCurrentLine(lineStartCouldBePropertyRegExp))
-                    return false;
+            if (testCurrentLine(lineStartCouldBePropertyRegExp))
+                return false;
 
-                return true;
-            };
+            return true;
+        };
 
-            let inProperty = () => {
-                if (!depth)
-                    return false;
-                return !testCurrentLine(inAtRuleRegExp) && !inSelector();
-            };
+        let inProperty = () => {
+            if (!depth)
+                return false;
+            return !testCurrentLine(inAtRuleRegExp) && !inSelector();
+        };
 
-            let formatBefore = () => {
-                if (this._builder.lastNewlineAppendWasMultiple && c === `}`)
-                    this._builder.removeLastNewline();
+        let formatBefore = () => {
+            if (this._builder.lastNewlineAppendWasMultiple && current === `}`)
+                this._builder.removeLastNewline();
 
-                if (dedentBefore.has(c))
-                    this._builder.dedent();
+            if (dedentBefore.has(current))
+                this._builder.dedent();
 
-                if (!this._builder.lastTokenWasNewline && newlineBefore.has(c))
-                    this._builder.appendNewline();
+            if (!this._builder.lastTokenWasNewline && newlineBefore.has(current))
+                this._builder.appendNewline();
 
-                if (!this._builder.lastTokenWasWhitespace && addSpaceBefore.has(c)) {
-                    let shouldAddSpaceBefore = () => {
-                        if (c === `(`) {
-                            if (testCurrentLine(inAtSupportsRuleRegExp))
+            if (!this._builder.lastTokenWasWhitespace && addSpaceBefore.has(current)) {
+                let shouldAddSpaceBefore = () => {
+                    if (current === `(`) {
+                        if (testCurrentLine(inAtSupportsRuleRegExp))
+                            return false;
+                        if (!testCurrentLine(inAtRuleRegExp))
+                            return false;
+                    }
+                    return true;
+                };
+                if (shouldAddSpaceBefore())
+                    this._builder.appendSpace();
+            }
+
+            while (this._builder.lastTokenWasWhitespace && removeSpaceBefore.has(current)) {
+                let shouldRemoveSpaceBefore = () => {
+                    if (current === `:`) {
+                        if (!testCurrentLine(this._builder.currentLine.includes(`(`) ? inAtRuleRegExp : inAtRuleBeforeParenthesisRegExp)) {
+                            if (!inProperty())
                                 return false;
-                            if (!testCurrentLine(inAtRuleRegExp))
+                        }
+                    }
+                    if (current === `(`) {
+                        if (!testCurrentLine(lastTokenWasOpenParenthesisRegExp)) {
+                            if (testCurrentLine(inAtRuleRegExp) && !testCurrentLine(inAtRuleAfterParenthesisRegExp))
                                 return false;
                         }
-                        return true;
-                    };
-                    if (shouldAddSpaceBefore())
-                        this._builder.appendSpace();
-                }
+                    }
+                    return true;
+                };
+                if (!shouldRemoveSpaceBefore())
+                    break;
+                this._builder.removeLastWhitespace();
+            }
+        };
 
-                while (this._builder.lastTokenWasWhitespace && removeSpaceBefore.has(c)) {
-                    let shouldRemoveSpaceBefore = () => {
-                        if (c === `:`) {
-                            if (!testCurrentLine(this._builder.currentLine.includes(`(`) ? inAtRuleRegExp : inAtRuleBeforeParenthesisRegExp)) {
+        let formatAfter = () => {
+            while (this._builder.lastTokenWasWhitespace && removeSpaceAfter.has(current)) {
+                let shouldRemoveSpaceAfter = () => {
+                    if (current === `(`) {
+                        if (!testCurrentLine(lastTokenWasOpenParenthesisRegExp)) {
+                            if (!testCurrentLine(inAtRuleRegExp)) {
                                 if (!inProperty())
                                     return false;
                             }
                         }
-                        if (c === `(`) {
-                            if (!testCurrentLine(lastTokenWasOpenParenthesisRegExp)) {
-                                if (testCurrentLine(inAtRuleRegExp) && !testCurrentLine(inAtRuleAfterParenthesisRegExp))
-                                    return false;
-                            }
-                        }
-                        return true;
-                    };
-                    if (!shouldRemoveSpaceBefore())
-                        break;
-                    this._builder.removeLastWhitespace();
-                }
-            };
+                    }
+                    return true;
+                };
+                if (!shouldRemoveSpaceAfter())
+                    break;
+                this._builder.removeLastWhitespace();
+            }
 
-            let formatAfter = () => {
-                while (this._builder.lastTokenWasWhitespace && removeSpaceAfter.has(c)) {
-                    let shouldRemoveSpaceAfter = () => {
-                        if (c === `(`) {
-                            if (!testCurrentLine(lastTokenWasOpenParenthesisRegExp)) {
-                                if (!testCurrentLine(inAtRuleRegExp)) {
-                                    if (!inProperty())
-                                        return false;
-                                }
-                            }
+            if (!this._builder.lastTokenWasWhitespace && addSpaceAfter.has(current)) {
+                let shouldAddSpaceAfter = () => {
+                    if (current === `:`) {
+                        if (!testCurrentLine(this._builder.currentLine.includes(`(`) ? inAtRuleRegExp : inAtRuleBeforeParenthesisRegExp)) {
+                            if (!inProperty())
+                                return false;
                         }
-                        return true;
-                    };
-                    if (!shouldRemoveSpaceAfter())
-                        break;
-                    this._builder.removeLastWhitespace();
-                }
-
-                if (!this._builder.lastTokenWasWhitespace && addSpaceAfter.has(c)) {
-                    let shouldAddSpaceAfter = () => {
-                        if (c === `:`) {
-                            if (!testCurrentLine(this._builder.currentLine.includes(`(`) ? inAtRuleRegExp : inAtRuleBeforeParenthesisRegExp)) {
-                                if (!inProperty())
-                                    return false;
-                            }
+                    }
+                    if (current === `)`) {
+                        if (!testCurrentLine(inAtRuleRegExp)) {
+                            if (!inProperty())
+                                return false;
                         }
-                        if (c === `)`) {
-                            if (!testCurrentLine(inAtRuleRegExp)) {
-                                if (!inProperty())
-                                    return false;
-                            }
-                        }
-                        return true;
-                    };
-                    if (shouldAddSpaceAfter())
-                        this._builder.appendSpace();
-                }
+                    }
+                    return true;
+                };
+                if (shouldAddSpaceAfter())
+                    this._builder.appendSpace();
+            }
 
-                if (indentAfter.has(c))
-                    this._builder.indent();
+            if (indentAfter.has(current))
+                this._builder.indent();
 
-                if (newlineAfter.has(c)) {
-                    if (c === `}`)
-                        this._builder.appendMultipleNewlines(2);
-                    else
-                        this._builder.appendNewline();
+            if (newlineAfter.has(current)) {
+                if (current === `}`)
+                    this._builder.appendMultipleNewlines(2);
+                else
+                    this._builder.appendNewline();
+            }
+        };
+
+        for (; index < this._sourceText.length; ++index) {
+            current = this._sourceText[index];
+
+            let possibleSpecialSequence = null;
+            if (quoteTypes.has(current))
+                possibleSpecialSequence = {type: "quote", startIndex: index, endString: current};
+            else if (current === `/` && this._sourceText[index + 1] === `*`)
+                possibleSpecialSequence = {type: "comment", startIndex: index, endString: `*/`};
+            else if (current === `u` && this._sourceText[index + 1] === `r` && this._sourceText[index + 2] === `l` && this._sourceText[index + 3] === `(`)
+                possibleSpecialSequence = {type: "url", startIndex: index, endString: `)`};
+
+            if (possibleSpecialSequence || specialSequenceStack.length) {
+                let currentSpecialSequence = specialSequenceStack.lastValue;
+
+                if (currentSpecialSequence?.type !== "comment") {
+                    if (possibleSpecialSequence?.type !== "comment") {
+                        let escapeCount = 0;
+                        while (this._sourceText[index - 1 - escapeCount] === "\\")
+                            ++escapeCount;
+                        if (escapeCount % 2)
+                            continue;
+                    }
+
+                    if (possibleSpecialSequence && (!currentSpecialSequence || currentSpecialSequence.type !== possibleSpecialSequence.type || currentSpecialSequence.endString !== possibleSpecialSequence.endString)) {
+                        specialSequenceStack.push(possibleSpecialSequence);
+                        continue;
+                    }
                 }
-            };
 
-            let specialSequenceEnd = null;
-            if (quoteTypes.has(c))
-                specialSequenceEnd = c;
-            else if (c === `/` && this._sourceText[i + 1] === `*`) {
-                inComment = true;
-                specialSequenceEnd = `*/`;
-            } else if (c === `u` && this._sourceText[i + 1] === `r` && this._sourceText[i + 2] === `l` && this._sourceText[i + 3] === `(`)
-                specialSequenceEnd = `)`;
+                if (Array.from(currentSpecialSequence.endString).some((item, i) => index + i < this._sourceText.length && this._sourceText[index - currentSpecialSequence.endString.length + i + 1] !== item))
+                    continue;
 
-            if (specialSequenceEnd) {
-                let startIndex = i;
-                let endIndex = i;
-                do {
-                    endIndex = this._sourceText.indexOf(specialSequenceEnd, endIndex + specialSequenceEnd.length);
-                } while (endIndex !== -1 && !inComment && this._sourceText[endIndex - 1] === `\\`);
+                let inComment = specialSequenceStack.some((item) => item.type === "comment");
 
-                if (endIndex === -1)
-                    endIndex = this._sourceText.length;
-                endIndex += specialSequenceEnd.length;
+                specialSequenceStack.pop();
+                if (specialSequenceStack.length)
+                    continue;
 
-                let specialSequenceText = this._sourceText.substring(startIndex, endIndex);
+                let specialSequenceText = this._sourceText.substring(currentSpecialSequence.startIndex, index + 1);
 
-                let lastSourceNewlineWasMultiple = this._sourceText[startIndex - 1] === `\n` && this._sourceText[startIndex - 2] === `\n`;
+                let lastSourceNewlineWasMultiple = this._sourceText[currentSpecialSequence.startIndex - 1] === `\n` && this._sourceText[currentSpecialSequence.startIndex - 2] === `\n`;
                 let lastAppendNewlineWasMultiple = this._builder.lastNewlineAppendWasMultiple;
 
                 let commentOnOwnLine = false;
@@ -264,7 +281,7 @@
                     }
 
                     if (commentOnOwnLine) {
-                        if (startIndex > 0 && !this._builder.indented)
+                        if (currentSpecialSequence.startIndex > 0 && !this._builder.indented)
                             this._builder.appendNewline();
                     } else if (this._builder.currentLine.length && !this._builder.lastTokenWasWhitespace)
                         this._builder.appendSpace();
@@ -273,11 +290,8 @@
                         this._builder.appendNewline(true);
                 }
 
-                this._builder.appendStringWithPossibleNewlines(specialSequenceText, startIndex);
+                this._builder.appendStringWithPossibleNewlines(specialSequenceText, currentSpecialSequence.startIndex);
 
-                i = endIndex;
-                c = this._sourceText[i];
-
                 if (inComment) {
                     if (commentOnOwnLine) {
                         if (lastAppendNewlineWasMultiple && !lastSourceNewlineWasMultiple)
@@ -284,7 +298,7 @@
                             this._builder.appendMultipleNewlines(2);
                         else
                             this._builder.appendNewline();
-                    } else if (!/\s/.test(c)) {
+                    } else if (!/\s/.test(current)) {
                         if (!testCurrentLine(inAtRuleRegExp) && !inSelector() && !inProperty())
                             this._builder.appendNewline();
                         else
@@ -292,15 +306,12 @@
                     }
                 }
 
-                --i; // Account for the iteration of the for loop.
-                c = this._sourceText[i];
-
                 formatAfter();
                 continue;
             }
 
-            if (/\s/.test(c)) {
-                if (c === `\n`) {
+            if (/\s/.test(current)) {
+                if (current === `\n`) {
                     if (!this._builder.lastTokenWasNewline) {
                         while (this._builder.lastTokenWasWhitespace)
                             this._builder.removeLastWhitespace();
@@ -314,16 +325,21 @@
                 continue;
             }
 
-            if (c === `{`)
+            if (current === `{`)
                 ++depth;
-            else if (c === `}`)
+            else if (current === `}`)
                 --depth;
 
             formatBefore();
-            this._builder.appendToken(c, i);
+            this._builder.appendToken(current, index);
             formatAfter();
         }
 
+        if (specialSequenceStack.length) {
+            let firstSpecialSequence = specialSequenceStack[0];
+            this._builder.appendStringWithPossibleNewlines(this._sourceText.substring(firstSpecialSequence.startIndex), firstSpecialSequence.startIndex);
+        }
+
         this._builder.finish();
     }
 };
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to