Skip to content

Commit 3454e18

Browse files
devrelmNV
authored andcommitted
Add support for CSSSupportsRule (#96)
1 parent d600816 commit 3454e18

File tree

6 files changed

+173
-11
lines changed

6 files changed

+173
-11
lines changed

docs/parse.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,9 @@ function inspect(object) {
131131
break;
132132

133133
default:
134-
root.appendChild(document.createTextNode(object.toString()));
134+
if (object) {
135+
root.appendChild(document.createTextNode(object.toString()));
136+
}
135137
}
136138
}
137139

lib/CSSSupportsRule.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//.CommonJS
2+
var CSSOM = {
3+
CSSRule: require("./CSSRule").CSSRule,
4+
};
5+
///CommonJS
6+
7+
8+
/**
9+
* @constructor
10+
* @see https://drafts.csswg.org/css-conditional-3/#the-csssupportsrule-interface
11+
*/
12+
CSSOM.CSSSupportsRule = function CSSSupportsRule() {
13+
CSSOM.CSSRule.call(this);
14+
this.conditionText = '';
15+
this.cssRules = [];
16+
};
17+
18+
CSSOM.CSSSupportsRule.prototype = new CSSOM.CSSRule();
19+
CSSOM.CSSSupportsRule.prototype.constructor = CSSOM.CSSSupportsRule;
20+
CSSOM.CSSSupportsRule.prototype.type = 12;
21+
22+
Object.defineProperty(CSSOM.CSSSupportsRule.prototype, "cssText", {
23+
get: function() {
24+
var cssTexts = [];
25+
26+
for (var i = 0, length = this.cssRules.length; i < length; i++) {
27+
cssTexts.push(this.cssRules[i].cssText);
28+
}
29+
30+
return "@supports " + this.conditionText + " {" + cssTexts.join("") + "}";
31+
}
32+
});
33+
34+
//.CommonJS
35+
exports.CSSSupportsRule = CSSOM.CSSSupportsRule;
36+
///CommonJS

lib/clone.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ var CSSOM = {
33
CSSStyleSheet: require("./CSSStyleSheet").CSSStyleSheet,
44
CSSStyleRule: require("./CSSStyleRule").CSSStyleRule,
55
CSSMediaRule: require("./CSSMediaRule").CSSMediaRule,
6+
CSSSupportsRule: require("./CSSSupportsRule").CSSSupportsRule,
67
CSSStyleDeclaration: require("./CSSStyleDeclaration").CSSStyleDeclaration,
78
CSSKeyframeRule: require('./CSSKeyframeRule').CSSKeyframeRule,
89
CSSKeyframesRule: require('./CSSKeyframesRule').CSSKeyframesRule
@@ -32,7 +33,8 @@ CSSOM.clone = function clone(stylesheet) {
3233
//5: CSSOM.CSSFontFaceRule,
3334
//6: CSSOM.CSSPageRule,
3435
8: CSSOM.CSSKeyframesRule,
35-
9: CSSOM.CSSKeyframeRule
36+
9: CSSOM.CSSKeyframeRule,
37+
12: CSSOM.CSSSupportsRule
3638
};
3739

3840
for (var i=0, rulesLength=rules.length; i < rulesLength; i++) {
@@ -62,6 +64,10 @@ CSSOM.clone = function clone(stylesheet) {
6264
ruleClone.mediaText = rule.mediaText;
6365
}
6466

67+
if (rule.hasOwnProperty('conditionText')) {
68+
ruleClone.conditionText = rule.conditionText;
69+
}
70+
6571
if (rule.hasOwnProperty('cssRules')) {
6672
ruleClone.cssRules = clone(rule).cssRules;
6773
}

lib/parse.js

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ CSSOM.parse = function parse(token) {
1515
"selector" or
1616
"atRule" or
1717
"atBlock" or
18+
"conditionBlock" or
1819
"before-name" or
1920
"name" or
2021
"before-value" or
@@ -34,18 +35,23 @@ CSSOM.parse = function parse(token) {
3435
"importRule-begin": true,
3536
"importRule": true,
3637
"atBlock": true,
38+
"conditionBlock": true,
3739
'documentRule-begin': true
3840
};
3941

4042
var styleSheet = new CSSOM.CSSStyleSheet();
4143

42-
// @type CSSStyleSheet|CSSMediaRule|CSSFontFaceRule|CSSKeyframesRule|CSSDocumentRule
44+
// @type CSSStyleSheet|CSSMediaRule|CSSSupportsRule|CSSFontFaceRule|CSSKeyframesRule|CSSDocumentRule
4345
var currentScope = styleSheet;
4446

45-
// @type CSSMediaRule|CSSKeyframesRule|CSSDocumentRule
47+
// @type CSSMediaRule|CSSSupportsRule|CSSKeyframesRule|CSSDocumentRule
4648
var parentRule;
4749

48-
var name, priority="", styleRule, mediaRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule;
50+
var ancestorRules = [];
51+
var hasAncestors = false;
52+
var prevScope;
53+
54+
var name, priority="", styleRule, mediaRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule;
4955

5056
var atKeyframesRegExp = /@(-(?:\w+-)+)?keyframes/g;
5157

@@ -151,6 +157,13 @@ CSSOM.parse = function parse(token) {
151157
i += "media".length;
152158
buffer = "";
153159
break;
160+
} else if (token.indexOf("@supports", i) === i) {
161+
state = "conditionBlock";
162+
supportsRule = new CSSOM.CSSSupportsRule();
163+
supportsRule.__starts = i;
164+
i += "supports".length;
165+
buffer = "";
166+
break;
154167
} else if (token.indexOf("@host", i) === i) {
155168
state = "hostRule-begin";
156169
i += "host".length;
@@ -196,17 +209,38 @@ CSSOM.parse = function parse(token) {
196209
state = "before-name";
197210
} else if (state === "atBlock") {
198211
mediaRule.media.mediaText = buffer.trim();
212+
213+
if (parentRule) {
214+
ancestorRules.push(parentRule);
215+
}
216+
199217
currentScope = parentRule = mediaRule;
200218
mediaRule.parentStyleSheet = styleSheet;
201219
buffer = "";
202220
state = "before-selector";
221+
} else if (state === "conditionBlock") {
222+
supportsRule.conditionText = buffer.trim();
223+
224+
if (parentRule) {
225+
ancestorRules.push(parentRule);
226+
}
227+
228+
currentScope = parentRule = supportsRule;
229+
supportsRule.parentStyleSheet = styleSheet;
230+
buffer = "";
231+
state = "before-selector";
203232
} else if (state === "hostRule-begin") {
233+
if (parentRule) {
234+
ancestorRules.push(parentRule);
235+
}
236+
204237
currentScope = parentRule = hostRule;
205238
hostRule.parentStyleSheet = styleSheet;
206239
buffer = "";
207240
state = "before-selector";
208241
} else if (state === "fontFaceRule-begin") {
209242
if (parentRule) {
243+
ancestorRules.push(parentRule);
210244
fontFaceRule.parentRule = parentRule;
211245
}
212246
fontFaceRule.parentStyleSheet = styleSheet;
@@ -216,6 +250,7 @@ CSSOM.parse = function parse(token) {
216250
} else if (state === "keyframesRule-begin") {
217251
keyframesRule.name = buffer.trim();
218252
if (parentRule) {
253+
ancestorRules.push(parentRule);
219254
keyframesRule.parentRule = parentRule;
220255
}
221256
keyframesRule.parentStyleSheet = styleSheet;
@@ -232,6 +267,7 @@ CSSOM.parse = function parse(token) {
232267
// FIXME: what if this '{' is in the url text of the match function?
233268
documentRule.matcher.matcherText = buffer.trim();
234269
if (parentRule) {
270+
ancestorRules.push(parentRule);
235271
documentRule.parentRule = parentRule;
236272
}
237273
currentScope = parentRule = documentRule;
@@ -345,15 +381,39 @@ CSSOM.parse = function parse(token) {
345381
case "keyframeRule-begin":
346382
case "before-selector":
347383
case "selector":
348-
// End of media/document rule.
384+
// End of media/supports/document rule.
349385
if (!parentRule) {
350386
parseError("Unexpected }");
351387
}
352-
currentScope.__ends = i + 1;
353-
// Nesting rules aren't supported yet
354-
styleSheet.cssRules.push(currentScope);
355-
currentScope = styleSheet;
356-
parentRule = null;
388+
389+
// Handle rules nested in @media or @supports
390+
hasAncestors = ancestorRules.length > 0;
391+
392+
while (ancestorRules.length > 0) {
393+
parentRule = ancestorRules.pop();
394+
395+
if (
396+
parentRule.constructor.name === "CSSMediaRule"
397+
|| parentRule.constructor.name === "CSSSupportsRule"
398+
) {
399+
prevScope = currentScope;
400+
currentScope = parentRule;
401+
currentScope.cssRules.push(prevScope);
402+
break;
403+
}
404+
405+
if (ancestorRules.length === 0) {
406+
hasAncestors = false;
407+
}
408+
}
409+
410+
if (!hasAncestors) {
411+
currentScope.__ends = i + 1;
412+
styleSheet.cssRules.push(currentScope);
413+
currentScope = styleSheet;
414+
parentRule = null;
415+
}
416+
357417
buffer = "";
358418
state = "before-selector";
359419
break;
@@ -393,6 +453,7 @@ CSSOM.CSSStyleSheet = require("./CSSStyleSheet").CSSStyleSheet;
393453
CSSOM.CSSStyleRule = require("./CSSStyleRule").CSSStyleRule;
394454
CSSOM.CSSImportRule = require("./CSSImportRule").CSSImportRule;
395455
CSSOM.CSSMediaRule = require("./CSSMediaRule").CSSMediaRule;
456+
CSSOM.CSSSupportsRule = require("./CSSSupportsRule").CSSSupportsRule;
396457
CSSOM.CSSFontFaceRule = require("./CSSFontFaceRule").CSSFontFaceRule;
397458
CSSOM.CSSHostRule = require("./CSSHostRule").CSSHostRule;
398459
CSSOM.CSSStyleDeclaration = require('./CSSStyleDeclaration').CSSStyleDeclaration;

spec/parse.spec.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,62 @@ var TESTS = [
676676
return result;
677677
})()
678678
},
679+
{
680+
input: "@supports (display: grid) { html { display: grid; } }",
681+
result: (function() {
682+
var result = {
683+
cssRules: [
684+
{
685+
conditionText: "(display: grid)",
686+
cssRules: [
687+
{
688+
selectorText: "html",
689+
style: {
690+
0: "display",
691+
display: "grid",
692+
length: 1
693+
},
694+
}
695+
],
696+
parentRule: null,
697+
}
698+
],
699+
parentStyleSheet: null
700+
};
701+
result.cssRules[0].parentStyleSheet = result.cssRules[0].cssRules[0].parentStyleSheet = result;
702+
result.cssRules[0].cssRules[0].parentRule = result.cssRules[0];
703+
result.cssRules[0].cssRules[0].style.parentRule = result.cssRules[0].cssRules[0];
704+
return result;
705+
})()
706+
},
707+
{
708+
input: "@supports not (display: grid) { html { display: flex; } }",
709+
result: (function() {
710+
var result = {
711+
cssRules: [
712+
{
713+
conditionText: "not (display: grid)",
714+
cssRules: [
715+
{
716+
selectorText: "html",
717+
style: {
718+
0: "display",
719+
display: "flex",
720+
length: 1
721+
},
722+
}
723+
],
724+
parentRule: null,
725+
}
726+
],
727+
parentStyleSheet: null
728+
};
729+
result.cssRules[0].parentStyleSheet = result.cssRules[0].cssRules[0].parentStyleSheet = result;
730+
result.cssRules[0].cssRules[0].parentRule = result.cssRules[0];
731+
result.cssRules[0].cssRules[0].style.parentRule = result.cssRules[0].cssRules[0];
732+
return result;
733+
})()
734+
},
679735
{
680736
input: '@import url(partial.css);\ni {font-style: italic}',
681737
result: (function() {

src/files.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ exports.files = [
44
"CSSStyleRule",
55
"MediaList",
66
"CSSMediaRule",
7+
"CSSSupportsRule",
78
"CSSImportRule",
89
"CSSFontFaceRule",
910
"CSSHostRule",

0 commit comments

Comments
 (0)