| Differences between
and this patch
- a/Source/WebInspectorUI/ChangeLog +14 lines
Lines 1-3 a/Source/WebInspectorUI/ChangeLog_sec1
1
2020-05-14  Devin Rousso  <drousso@apple.com>
2
3
        Web Inspector: Fails to pretty-print a particular CSS file
4
        https://bugs.webkit.org/show_bug.cgi?id=211930
5
6
        Reviewed by NOBODY (OOPS!).
7
8
        * UserInterface/Workers/Formatter/CSSFormatter.js:
9
        (CSSFormatter.prototype._format):
10
        Keep a stack of special sequences (e.g. `"`, `'`, `/*`, etc.), only outputting the text in
11
        between the start and end of the sequence when the stack is empty. Ignore all other special
12
        sequences when in a comma sequence. Add proper checks for if the star/end is escaped.
13
        Drive-by: minor refactor so that the arrow functions are created outside the loop.
14
1
2020-05-13  Devin Rousso  <drousso@apple.com>
15
2020-05-13  Devin Rousso  <drousso@apple.com>
2
16
3
        Web Inspector: rename CSS.StyleSheetOrigin.Regular to CSS.StyleSheetOrigin.Author to match the spec
17
        Web Inspector: rename CSS.StyleSheetOrigin.Regular to CSS.StyleSheetOrigin.Author to match the spec
- a/Source/WebInspectorUI/UserInterface/Workers/Formatter/CSSFormatter.js -150 / +161 lines
Lines 91-257 CSSFormatter = class CSSFormatter a/Source/WebInspectorUI/UserInterface/Workers/Formatter/CSSFormatter.js_sec1
91
        const lastTokenWasOpenParenthesisRegExp = /\(\s*$/;
91
        const lastTokenWasOpenParenthesisRegExp = /\(\s*$/;
92
92
93
        let depth = 0;
93
        let depth = 0;
94
        let specialSequenceStack = [];
94
95
95
        for (let i = 0; i < this._sourceText.length; ++i) {
96
        let index = 0;
96
            let c = this._sourceText[i];
97
        let current = null;
97
98
98
            let testCurrentLine = (regExp) => regExp.test(this._builder.currentLine);
99
        let testCurrentLine = (regExp) => regExp.test(this._builder.currentLine);
99
100
100
            let inComment = false;
101
        let inSelector = () => {
101
102
            let nextOpenBrace = this._sourceText.indexOf(`{`, index);
102
            let inSelector = () => {
103
            if (nextOpenBrace !== -1) {
103
                let nextOpenBrace = this._sourceText.indexOf(`{`, i);
104
                let nextQuote = Infinity;
104
                if (nextOpenBrace !== -1) {
105
                for (let quoteType of quoteTypes) {
105
                    let nextQuote = Infinity;
106
                    let quoteIndex = this._sourceText.indexOf(quoteType, index);
106
                    for (let quoteType of quoteTypes) {
107
                    if (quoteIndex !== -1 && quoteIndex < nextQuote)
107
                        let quoteIndex = this._sourceText.indexOf(quoteType, i);
108
                        nextQuote = quoteIndex;
108
                        if (quoteIndex !== -1 && quoteIndex < nextQuote)
109
                            nextQuote = quoteIndex;
110
                    }
111
                    if (nextOpenBrace < nextQuote) {
112
                        let nextSemicolon = this._sourceText.indexOf(`;`, i);
113
                        if (nextSemicolon === -1)
114
                            nextSemicolon = Infinity;
115
116
                        let nextNewline = this._sourceText.indexOf(`\n`, i);
117
                        if (nextNewline === -1)
118
                            nextNewline = Infinity;
119
120
                        if (nextOpenBrace < Math.min(nextSemicolon, nextNewline))
121
                            return true;
122
                    }
123
                }
109
                }
110
                if (nextOpenBrace < nextQuote) {
111
                    let nextSemicolon = this._sourceText.indexOf(`;`, index);
112
                    if (nextSemicolon === -1)
113
                        nextSemicolon = Infinity;
124
114
125
                if (testCurrentLine(lineStartCouldBePropertyRegExp))
115
                    let nextNewline = this._sourceText.indexOf(`\n`, index);
126
                    return false;
116
                    if (nextNewline === -1)
117
                        nextNewline = Infinity;
127
118
128
                return true;
119
                    if (nextOpenBrace < Math.min(nextSemicolon, nextNewline))
129
            };
120
                        return true;
121
                }
122
            }
130
123
131
            let inProperty = () => {
124
            if (testCurrentLine(lineStartCouldBePropertyRegExp))
132
                if (!depth)
125
                return false;
133
                    return false;
134
                return !testCurrentLine(inAtRuleRegExp) && !inSelector();
135
            };
136
126
137
            let formatBefore = () => {
127
            return true;
138
                if (this._builder.lastNewlineAppendWasMultiple && c === `}`)
128
        };
139
                    this._builder.removeLastNewline();
140
129
141
                if (dedentBefore.has(c))
130
        let inProperty = () => {
142
                    this._builder.dedent();
131
            if (!depth)
132
                return false;
133
            return !testCurrentLine(inAtRuleRegExp) && !inSelector();
134
        };
143
135
144
                if (!this._builder.lastTokenWasNewline && newlineBefore.has(c))
136
        let formatBefore = () => {
145
                    this._builder.appendNewline();
137
            if (this._builder.lastNewlineAppendWasMultiple && current === `}`)
138
                this._builder.removeLastNewline();
146
139
147
                if (!this._builder.lastTokenWasWhitespace && addSpaceBefore.has(c)) {
140
            if (dedentBefore.has(current))
148
                    let shouldAddSpaceBefore = () => {
141
                this._builder.dedent();
149
                        if (c === `(`) {
142
150
                            if (testCurrentLine(inAtSupportsRuleRegExp))
143
            if (!this._builder.lastTokenWasNewline && newlineBefore.has(current))
151
                                return false;
144
                this._builder.appendNewline();
152
                            if (!testCurrentLine(inAtRuleRegExp))
145
146
            if (!this._builder.lastTokenWasWhitespace && addSpaceBefore.has(current)) {
147
                let shouldAddSpaceBefore = () => {
148
                    if (current === `(`) {
149
                        if (testCurrentLine(inAtSupportsRuleRegExp))
150
                            return false;
151
                        if (!testCurrentLine(inAtRuleRegExp))
152
                            return false;
153
                    }
154
                    return true;
155
                };
156
                if (shouldAddSpaceBefore())
157
                    this._builder.appendSpace();
158
            }
159
160
            while (this._builder.lastTokenWasWhitespace && removeSpaceBefore.has(current)) {
161
                let shouldRemoveSpaceBefore = () => {
162
                    if (current === `:`) {
163
                        if (!testCurrentLine(this._builder.currentLine.includes(`(`) ? inAtRuleRegExp : inAtRuleBeforeParenthesisRegExp)) {
164
                            if (!inProperty())
153
                                return false;
165
                                return false;
154
                        }
166
                        }
155
                        return true;
167
                    }
156
                    };
168
                    if (current === `(`) {
157
                    if (shouldAddSpaceBefore())
169
                        if (!testCurrentLine(lastTokenWasOpenParenthesisRegExp)) {
158
                        this._builder.appendSpace();
170
                            if (testCurrentLine(inAtRuleRegExp) && !testCurrentLine(inAtRuleAfterParenthesisRegExp))
159
                }
171
                                return false;
172
                        }
173
                    }
174
                    return true;
175
                };
176
                if (!shouldRemoveSpaceBefore())
177
                    break;
178
                this._builder.removeLastWhitespace();
179
            }
180
        };
160
181
161
                while (this._builder.lastTokenWasWhitespace && removeSpaceBefore.has(c)) {
182
        let formatAfter = () => {
162
                    let shouldRemoveSpaceBefore = () => {
183
            while (this._builder.lastTokenWasWhitespace && removeSpaceAfter.has(current)) {
163
                        if (c === `:`) {
184
                let shouldRemoveSpaceAfter = () => {
164
                            if (!testCurrentLine(this._builder.currentLine.includes(`(`) ? inAtRuleRegExp : inAtRuleBeforeParenthesisRegExp)) {
185
                    if (current === `(`) {
165
                                if (!inProperty())
186
                        if (!testCurrentLine(lastTokenWasOpenParenthesisRegExp)) {
166
                                    return false;
167
                            }
168
                        }
169
                        if (c === `(`) {
170
                            if (!testCurrentLine(lastTokenWasOpenParenthesisRegExp)) {
171
                                if (testCurrentLine(inAtRuleRegExp) && !testCurrentLine(inAtRuleAfterParenthesisRegExp))
172
                                    return false;
173
                            }
174
                        }
175
                        return true;
176
                    };
177
                    if (!shouldRemoveSpaceBefore())
178
                        break;
179
                    this._builder.removeLastWhitespace();
180
                }
181
            };
182
183
            let formatAfter = () => {
184
                while (this._builder.lastTokenWasWhitespace && removeSpaceAfter.has(c)) {
185
                    let shouldRemoveSpaceAfter = () => {
186
                        if (c === `(`) {
187
                            if (!testCurrentLine(lastTokenWasOpenParenthesisRegExp)) {
188
                                if (!testCurrentLine(inAtRuleRegExp)) {
189
                                    if (!inProperty())
190
                                        return false;
191
                                }
192
                            }
193
                        }
194
                        return true;
195
                    };
196
                    if (!shouldRemoveSpaceAfter())
197
                        break;
198
                    this._builder.removeLastWhitespace();
199
                }
200
201
                if (!this._builder.lastTokenWasWhitespace && addSpaceAfter.has(c)) {
202
                    let shouldAddSpaceAfter = () => {
203
                        if (c === `:`) {
204
                            if (!testCurrentLine(this._builder.currentLine.includes(`(`) ? inAtRuleRegExp : inAtRuleBeforeParenthesisRegExp)) {
205
                                if (!inProperty())
206
                                    return false;
207
                            }
208
                        }
209
                        if (c === `)`) {
210
                            if (!testCurrentLine(inAtRuleRegExp)) {
187
                            if (!testCurrentLine(inAtRuleRegExp)) {
211
                                if (!inProperty())
188
                                if (!inProperty())
212
                                    return false;
189
                                    return false;
213
                            }
190
                            }
214
                        }
191
                        }
215
                        return true;
192
                    }
216
                    };
193
                    return true;
217
                    if (shouldAddSpaceAfter())
194
                };
218
                        this._builder.appendSpace();
195
                if (!shouldRemoveSpaceAfter())
196
                    break;
197
                this._builder.removeLastWhitespace();
198
            }
199
200
            if (!this._builder.lastTokenWasWhitespace && addSpaceAfter.has(current)) {
201
                let shouldAddSpaceAfter = () => {
202
                    if (current === `:`) {
203
                        if (!testCurrentLine(this._builder.currentLine.includes(`(`) ? inAtRuleRegExp : inAtRuleBeforeParenthesisRegExp)) {
204
                            if (!inProperty())
205
                                return false;
206
                        }
207
                    }
208
                    if (current === `)`) {
209
                        if (!testCurrentLine(inAtRuleRegExp)) {
210
                            if (!inProperty())
211
                                return false;
212
                        }
213
                    }
214
                    return true;
215
                };
216
                if (shouldAddSpaceAfter())
217
                    this._builder.appendSpace();
218
            }
219
220
            if (indentAfter.has(current))
221
                this._builder.indent();
222
223
            if (newlineAfter.has(current)) {
224
                if (current === `}`)
225
                    this._builder.appendMultipleNewlines(2);
226
                else
227
                    this._builder.appendNewline();
228
            }
229
        };
230
231
        for (; index < this._sourceText.length; ++index) {
232
            current = this._sourceText[index];
233
234
            let possibleSpecialSequence = null;
235
            if (quoteTypes.has(current))
236
                possibleSpecialSequence = {type: "quote", startIndex: index, endString: current};
237
            else if (current === `/` && this._sourceText[index + 1] === `*`)
238
                possibleSpecialSequence = {type: "comment", startIndex: index, endString: `*/`};
239
            else if (current === `u` && this._sourceText[index + 1] === `r` && this._sourceText[index + 2] === `l` && this._sourceText[index + 3] === `(`)
240
                possibleSpecialSequence = {type: "url", startIndex: index, endString: `)`};
241
242
            if (possibleSpecialSequence || specialSequenceStack.length) {
243
                let currentSpecialSequence = specialSequenceStack.lastValue;
244
245
                if (currentSpecialSequence?.type !== "comment") {
246
                    if (possibleSpecialSequence?.type !== "comment") {
247
                        let escapeCount = 0;
248
                        while (this._sourceText[index - 1 - escapeCount] === "\\")
249
                            ++escapeCount;
250
                        if (escapeCount % 2)
251
                            continue;
252
                    }
253
254
                    if (possibleSpecialSequence && (!currentSpecialSequence || currentSpecialSequence.type !== possibleSpecialSequence.type || currentSpecialSequence.endString !== possibleSpecialSequence.endString)) {
255
                        specialSequenceStack.push(possibleSpecialSequence);
256
                        continue;
257
                    }
219
                }
258
                }
220
259
221
                if (indentAfter.has(c))
260
                if (Array.from(currentSpecialSequence.endString).some((item, i) => index + i < this._sourceText.length && this._sourceText[index - currentSpecialSequence.endString.length + i + 1] !== item))
222
                    this._builder.indent();
261
                    continue;
223
262
224
                if (newlineAfter.has(c)) {
263
                let inComment = specialSequenceStack.some((item) => item.type === "comment");
225
                    if (c === `}`)
226
                        this._builder.appendMultipleNewlines(2);
227
                    else
228
                        this._builder.appendNewline();
229
                }
230
            };
231
264
232
            let specialSequenceEnd = null;
265
                specialSequenceStack.pop();
233
            if (quoteTypes.has(c))
266
                if (specialSequenceStack.length)
234
                specialSequenceEnd = c;
267
                    continue;
235
            else if (c === `/` && this._sourceText[i + 1] === `*`) {
236
                inComment = true;
237
                specialSequenceEnd = `*/`;
238
            } else if (c === `u` && this._sourceText[i + 1] === `r` && this._sourceText[i + 2] === `l` && this._sourceText[i + 3] === `(`)
239
                specialSequenceEnd = `)`;
240
268
241
            if (specialSequenceEnd) {
269
                let specialSequenceText = this._sourceText.substring(currentSpecialSequence.startIndex, index + 1);
242
                let startIndex = i;
243
                let endIndex = i;
244
                do {
245
                    endIndex = this._sourceText.indexOf(specialSequenceEnd, endIndex + specialSequenceEnd.length);
246
                } while (endIndex !== -1 && !inComment && this._sourceText[endIndex - 1] === `\\`);
247
270
248
                if (endIndex === -1)
271
                let lastSourceNewlineWasMultiple = this._sourceText[currentSpecialSequence.startIndex - 1] === `\n` && this._sourceText[currentSpecialSequence.startIndex - 2] === `\n`;
249
                    endIndex = this._sourceText.length;
250
                endIndex += specialSequenceEnd.length;
251
252
                let specialSequenceText = this._sourceText.substring(startIndex, endIndex);
253
254
                let lastSourceNewlineWasMultiple = this._sourceText[startIndex - 1] === `\n` && this._sourceText[startIndex - 2] === `\n`;
255
                let lastAppendNewlineWasMultiple = this._builder.lastNewlineAppendWasMultiple;
272
                let lastAppendNewlineWasMultiple = this._builder.lastNewlineAppendWasMultiple;
256
273
257
                let commentOnOwnLine = false;
274
                let commentOnOwnLine = false;
Lines 264-270 CSSFormatter = class CSSFormatter a/Source/WebInspectorUI/UserInterface/Workers/Formatter/CSSFormatter.js_sec2
264
                    }
281
                    }
265
282
266
                    if (commentOnOwnLine) {
283
                    if (commentOnOwnLine) {
267
                        if (startIndex > 0 && !this._builder.indented)
284
                        if (currentSpecialSequence.startIndex > 0 && !this._builder.indented)
268
                            this._builder.appendNewline();
285
                            this._builder.appendNewline();
269
                    } else if (this._builder.currentLine.length && !this._builder.lastTokenWasWhitespace)
286
                    } else if (this._builder.currentLine.length && !this._builder.lastTokenWasWhitespace)
270
                        this._builder.appendSpace();
287
                        this._builder.appendSpace();
Lines 273-282 CSSFormatter = class CSSFormatter a/Source/WebInspectorUI/UserInterface/Workers/Formatter/CSSFormatter.js_sec3
273
                        this._builder.appendNewline(true);
290
                        this._builder.appendNewline(true);
274
                }
291
                }
275
292
276
                this._builder.appendStringWithPossibleNewlines(specialSequenceText, startIndex);
293
                this._builder.appendStringWithPossibleNewlines(specialSequenceText, currentSpecialSequence.start);
277
278
                i = endIndex;
279
                c = this._sourceText[i];
280
294
281
                if (inComment) {
295
                if (inComment) {
282
                    if (commentOnOwnLine) {
296
                    if (commentOnOwnLine) {
Lines 284-290 CSSFormatter = class CSSFormatter a/Source/WebInspectorUI/UserInterface/Workers/Formatter/CSSFormatter.js_sec4
284
                            this._builder.appendMultipleNewlines(2);
298
                            this._builder.appendMultipleNewlines(2);
285
                        else
299
                        else
286
                            this._builder.appendNewline();
300
                            this._builder.appendNewline();
287
                    } else if (!/\s/.test(c)) {
301
                    } else if (!/\s/.test(current)) {
288
                        if (!testCurrentLine(inAtRuleRegExp) && !inSelector() && !inProperty())
302
                        if (!testCurrentLine(inAtRuleRegExp) && !inSelector() && !inProperty())
289
                            this._builder.appendNewline();
303
                            this._builder.appendNewline();
290
                        else
304
                        else
Lines 292-306 CSSFormatter = class CSSFormatter a/Source/WebInspectorUI/UserInterface/Workers/Formatter/CSSFormatter.js_sec5
292
                    }
306
                    }
293
                }
307
                }
294
308
295
                --i; // Account for the iteration of the for loop.
296
                c = this._sourceText[i];
297
298
                formatAfter();
309
                formatAfter();
299
                continue;
310
                continue;
300
            }
311
            }
301
312
302
            if (/\s/.test(c)) {
313
            if (/\s/.test(current)) {
303
                if (c === `\n`) {
314
                if (current === `\n`) {
304
                    if (!this._builder.lastTokenWasNewline) {
315
                    if (!this._builder.lastTokenWasNewline) {
305
                        while (this._builder.lastTokenWasWhitespace)
316
                        while (this._builder.lastTokenWasWhitespace)
306
                            this._builder.removeLastWhitespace();
317
                            this._builder.removeLastWhitespace();
Lines 314-326 CSSFormatter = class CSSFormatter a/Source/WebInspectorUI/UserInterface/Workers/Formatter/CSSFormatter.js_sec6
314
                continue;
325
                continue;
315
            }
326
            }
316
327
317
            if (c === `{`)
328
            if (current === `{`)
318
                ++depth;
329
                ++depth;
319
            else if (c === `}`)
330
            else if (current === `}`)
320
                --depth;
331
                --depth;
321
332
322
            formatBefore();
333
            formatBefore();
323
            this._builder.appendToken(c, i);
334
            this._builder.appendToken(current, index);
324
            formatAfter();
335
            formatAfter();
325
        }
336
        }
326
337
- a/LayoutTests/ChangeLog +12 lines
Lines 1-3 a/LayoutTests/ChangeLog_sec1
1
2020-05-14  Devin Rousso  <drousso@apple.com>
2
3
        Web Inspector: Fails to pretty-print a particular CSS file
4
        https://bugs.webkit.org/show_bug.cgi?id=211930
5
6
        Reviewed by NOBODY (OOPS!).
7
8
        * inspector/formatting/formatting-css.html:
9
        * inspector/formatting/formatting-css-expected.txt:
10
        * inspector/formatting/resources/css-tests/url.css: Added.
11
        * inspector/formatting/resources/css-tests/url-expected.css: Added.
12
1
2020-05-14  Jason Lawrence  <lawrence.j@apple.com>
13
2020-05-14  Jason Lawrence  <lawrence.j@apple.com>
2
14
3
        [ Mac wk1 Debug ] imported/w3c/web-platform-tests/fetch/api/basic/stream-safe-creation.any.html is flaky crashing with alerts - WTFCrashWithInfo - SC::JSObject::get(JSC::JSGlobalObject*, JSC::PropertyName)
15
        [ Mac wk1 Debug ] imported/w3c/web-platform-tests/fetch/api/basic/stream-safe-creation.any.html is flaky crashing with alerts - WTFCrashWithInfo - SC::JSObject::get(JSC::JSGlobalObject*, JSC::PropertyName)
- a/LayoutTests/inspector/formatting/formatting-css-expected.txt +1 lines
Lines 9-13 PASS: gradient.css a/LayoutTests/inspector/formatting/formatting-css-expected.txt_sec1
9
PASS: keyframes.css
9
PASS: keyframes.css
10
PASS: media-query.css
10
PASS: media-query.css
11
PASS: selectors.css
11
PASS: selectors.css
12
PASS: url.css
12
PASS: wrapping.css
13
PASS: wrapping.css
13
14
- a/LayoutTests/inspector/formatting/formatting-css.html +1 lines
Lines 15-20 function test() a/LayoutTests/inspector/formatting/formatting-css.html_sec1
15
        "resources/css-tests/keyframes.css",
15
        "resources/css-tests/keyframes.css",
16
        "resources/css-tests/media-query.css",
16
        "resources/css-tests/media-query.css",
17
        "resources/css-tests/selectors.css",
17
        "resources/css-tests/selectors.css",
18
        "resources/css-tests/url.css",
18
        "resources/css-tests/wrapping.css",
19
        "resources/css-tests/wrapping.css",
19
    ]);
20
    ]);
20
21
- a/LayoutTests/inspector/formatting/resources/css-tests/url-expected.css +27 lines
Line 0 a/LayoutTests/inspector/formatting/resources/css-tests/url-expected.css_sec1
1
body {
2
    background: url(basic);
3
    background: url(with space);
4
    background: url('  single  ');
5
    background: url("  double  ");
6
    background: url('  nested  "  double  "  single  ');
7
    background: url("  nested  '  single  '  double  ");
8
    background: url('  escaped  \'  single  \'  single  ');
9
    background: url("  escaped  \"  double  \"  double  ");
10
    background: url('  escaped  \\\'  single  \\\'  single  ');
11
    background: url("  escaped  \\\"  double  \\\"  double  ");
12
    background: url('  nested  /*  comment  */  single  ');
13
    background: url("  nested  /*  comment  */  dobule  ");
14
    background: url( /*  before  */ '  single  ' /*  after  */);
15
    background: url( /*  before  */ "  double  " /*  after  */);
16
}
17
18
body {
19
    background-size: cover;
20
    background-repeat: no-repeat;
21
    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='data:image/jpeg;base64,abcdef'/%3E%3C/g%3E%3C/svg%3E")
22
}
23
24
body {
25
    background-image: linear-gradient(180deg, white, rgba(0, 0, 0, 0) 200px);
26
    min-height: 200px
27
}
- a/LayoutTests/inspector/formatting/resources/css-tests/url.css +24 lines
Line 0 a/LayoutTests/inspector/formatting/resources/css-tests/url.css_sec1
1
body {
2
    background  :  url  (  basic  )  ;
3
    background  :  url  (  with space  )  ;
4
5
    background  :  url  (  '  single  '  )  ;
6
    background  :  url  (  "  double  "  )  ;
7
8
    background  :  url  (  '  nested  "  double  "  single  '  )  ;
9
    background  :  url  (  "  nested  '  single  '  double  "  )  ;
10
11
    background  :  url  (  '  escaped  \'  single  \'  single  '  )  ;
12
    background  :  url  (  "  escaped  \"  double  \"  double  "  )  ;
13
14
    background  :  url  (  '  escaped  \\\'  single  \\\'  single  '  )  ;
15
    background  :  url  (  "  escaped  \\\"  double  \\\"  double  "  )  ;
16
17
    background  :  url  (  '  nested  /*  comment  */  single  '  )  ;
18
    background  :  url  (  "  nested  /*  comment  */  dobule  "  )  ;
19
20
    background  :  url  (  /*  before  */  '  single  '  /*  after  */  )  ;
21
    background  :  url  (  /*  before  */  "  double  "  /*  after  */  )  ;
22
}
23
24
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='data:image/jpeg;base64,abcdef'/%3E%3C/g%3E%3C/svg%3E")}body{background-image:linear-gradient(180deg,white,rgba(0,0,0,0) 200px);min-height:200px}

Return to Bug 211930