Skip to content

Commit 56fa29b

Browse files
committed
Wrap declarations after/in nested group rules in CSSNestedDeclarations
Declarations are no longer hoisted or wrapped in a style rule. Some problem remains... 1. @font-feature-values, @function, @page, can also have declarations interleaved with rules, but CSSNestedDeclarations is currently associated to style properties. (w3c/csswg-drafts#11272) 2. "}" should be ignored in a "style" attribute value (a declaration block) but the algorithm has been replaced with the same algorithm than for a block of rules and declarations. (w3c/csswg-drafts#11113) 3. The current text wants declarations interleaved by an invalid at-rule or an invalid (qualified) rule error to be separated, but the reason is not stated and no browser does this. (w3c/csswg-drafts#11271) 4. The current text clearly has some other minor typos and is not clear about whether rules and declaration, or only rules, should be returned. (w3c/csswg-drafts#11017)
1 parent 70911e3 commit 56fa29b

25 files changed

+385
-260
lines changed

__tests__/stylesheet.js

+192-159
Large diffs are not rendered by default.

lib/cssom/CSSColorProfileRule-impl.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class CSSColorProfileRuleImpl extends CSSRuleImpl {
2020
constructor(globalObject, args, privateData) {
2121
super(globalObject, args, privateData)
2222
const { prelude, value } = privateData
23-
const { declarations } = parseBlockContents(value, this)
23+
const [declarations = []] = parseBlockContents(value, this)
2424
this.name = serializeCSSComponentValue(prelude)
2525
descriptorNames.forEach(name => {
2626
const declaration = declarations.find(declaration => declaration.name === name)

lib/cssom/CSSConditionRule-impl.js

+12-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
const { implementation: CSSGroupingRuleImpl } = require('./CSSGroupingRule-impl.js')
3+
const CSSNestedDeclarations = require('./CSSNestedDeclarations.js')
34
const CSSRuleList = require('./CSSRuleList.js')
4-
const CSSStyleRule = require('./CSSStyleRule.js')
55
const { parseBlockContents } = require('../parse/parser.js')
66
const { serializeCSSComponentValue } = require('../serialize.js')
77

@@ -17,16 +17,19 @@ class CSSConditionRuleImpl extends CSSGroupingRuleImpl {
1717
*/
1818
constructor(globalObject, args, privateData) {
1919
super(globalObject, args, privateData)
20+
const { parentStyleSheet } = this
2021
const { prelude, value } = privateData
21-
const { declarations, rules } = parseBlockContents(value, this)
22+
const rules = parseBlockContents(value, this).map(rule => {
23+
if (Array.isArray(rule) ) {
24+
return CSSNestedDeclarations.createImpl(globalObject, undefined, {
25+
declarations: rule,
26+
parentRule: this,
27+
parentStyleSheet,
28+
})
29+
}
30+
return rule
31+
})
2232
this._condition = prelude
23-
// Wrap declarations in nested style rule
24-
if (0 < declarations.length) {
25-
const data = { parentRule: this, parentStyleSheet: this.parentStyleSheet }
26-
const styleRule = CSSStyleRule.createImpl(globalObject, undefined, data)
27-
styleRule.style._declarations.push(...declarations)
28-
rules.unshift(styleRule)
29-
}
3033
this.cssRules = CSSRuleList.createImpl(globalObject, undefined, { rules })
3134
}
3235

lib/cssom/CSSCounterStyleRule-impl.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class CSSCounterStyleRuleImpl extends CSSRuleImpl {
2222
constructor(globalObject, args, privateData) {
2323
super(globalObject, args, privateData)
2424
const { prelude, value } = privateData
25-
const { declarations } = parseBlockContents(value, this)
25+
const [declarations = []] = parseBlockContents(value, this)
2626
this._name = serializeCSSComponentValue(prelude)
2727
declarations.forEach(({ name, value }) => this[`_${cssPropertyToIDLAttribute(name)}`] = value)
2828
}

lib/cssom/CSSFontFaceRule-impl.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class CSSFontFaceRuleImpl extends CSSRuleImpl {
1515
*/
1616
constructor(globalObject, args, privateData) {
1717
super(globalObject, args, privateData)
18-
const { declarations } = parseBlockContents(privateData.value, this)
18+
const [declarations = []] = parseBlockContents(privateData.value, this)
1919
this.style = CSSFontFaceDescriptors.createImpl(globalObject, undefined, { declarations, parentRule: this })
2020
}
2121

lib/cssom/CSSFontFeatureValuesMap-impl.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class CSSFontFeatureValuesMapImpl {
2626
this.parentRule = parentRule
2727
this.parentStyleSheet = parentStyleSheet
2828
if (value) {
29-
const { declarations } = parseBlockContents(value, this)
29+
const [declarations = []] = parseBlockContents(value, this)
3030
const entries = declarations.map(({ name, value }) =>
3131
[name, Array.isArray(value) ? [...value.map(component => component.value)] : [value.value]])
3232
this._map = new Map(entries)

lib/cssom/CSSFontFeatureValuesRule-impl.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
const { serializeCSSComponentValueList, serializeCSSValue } = require('../serialize.js')
33
const CSSFontFeatureValuesMap = require('./CSSFontFeatureValuesMap.js')
4+
const CSSNestedDeclarations = require('./CSSNestedDeclarations.js')
45
const { implementation: CSSRuleImpl } = require('./CSSRule-impl.js')
56
const { cssPropertyToIDLAttribute } = require('../utils/string.js')
67
const { parseBlockContents } = require('../parse/parser.js')
@@ -22,7 +23,18 @@ class CSSFontFeatureValuesRuleImpl extends CSSRuleImpl {
2223
super(globalObject, args, privateData)
2324
const { parentStyleSheet } = this
2425
const { prelude, value } = privateData
25-
const { declarations, rules } = parseBlockContents(value, this)
26+
const contents = parseBlockContents(value, this)
27+
const declarations = Array.isArray(contents[0]) ? contents.shift() : []
28+
const rules = contents.map(rule => {
29+
if (Array.isArray(rule) ) {
30+
return CSSNestedDeclarations.createImpl(globalObject, undefined, {
31+
declarations,
32+
parentRule: this,
33+
parentStyleSheet,
34+
})
35+
}
36+
return rule
37+
})
2638
this.fontFamily = serializeCSSComponentValueList(prelude)
2739
declarations.forEach(declaration =>
2840
this[cssPropertyToIDLAttribute(declaration.name)] = serializeCSSValue(declaration))

lib/cssom/CSSFontPaletteValuesRule-impl.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class CSSFontPaletteValuesRuleImpl extends CSSRuleImpl {
2020
constructor(globalObject, args, privateData) {
2121
super(globalObject, args, privateData)
2222
const { prelude, value } = privateData
23-
const { declarations } = parseBlockContents(value, this)
23+
const [declarations = []] = parseBlockContents(value, this)
2424
this.name = serializeCSSComponentValue(prelude)
2525
descriptorNames.forEach(name => {
2626
const declaration = declarations.find(declaration => declaration.name === name)

lib/cssom/CSSFunctionRule-impl.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
const { implementation: CSSGroupingRuleImpl } = require('./CSSGroupingRule-impl.js')
3+
const CSSNestedDeclarations = require('./CSSNestedDeclarations.js')
34
const CSSRuleList = require('./CSSRuleList.js')
45
const { parseBlockContents } = require('../parse/parser.js')
56
const { serializeCSSComponentValueList } = require('../serialize.js')
@@ -16,8 +17,18 @@ class CSSFunctionRuleImpl extends CSSGroupingRuleImpl {
1617
*/
1718
constructor(globalObject, args, privateData) {
1819
super(globalObject, args, privateData)
20+
const { parentStyleSheet } = this
1921
const { prelude, value } = privateData
20-
const { rules } = parseBlockContents(value, this)
22+
const rules = parseBlockContents(value, this).map(rule => {
23+
if (Array.isArray(rule) ) {
24+
return CSSNestedDeclarations.createImpl(globalObject, undefined, {
25+
declarations: rule,
26+
parentRule: this,
27+
parentStyleSheet,
28+
})
29+
}
30+
return rule
31+
})
2132
this._prelude = serializeCSSComponentValueList(prelude)
2233
this.cssRules = CSSRuleList.createImpl(globalObject, undefined, { rules })
2334
}

lib/cssom/CSSKeyframeRule-impl.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class CSSKeyframeRuleImpl extends CSSRuleImpl {
2323
constructor(globalObject, args, privateData) {
2424
super(globalObject, args, privateData)
2525
const { prelude, value } = privateData
26-
const { declarations } = parseBlockContents(value, this)
26+
const [declarations = []] = parseBlockContents(value, this)
2727
this._keyText = prelude
2828
this.style = CSSKeyframeProperties.createImpl(globalObject, undefined, { declarations, parentRule: this })
2929
}

lib/cssom/CSSKeyframesRule-impl.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class CSSKeyframesRuleImpl extends CSSRuleImpl {
4242
constructor(globalObject, args, privateData) {
4343
super(globalObject, args, privateData)
4444
const { prelude, value } = privateData
45-
const { rules } = parseBlockContents(value, this)
45+
const rules = parseBlockContents(value, this)
4646
this._name = prelude
4747
this.cssRules = CSSRuleList.createImpl(globalObject, undefined, { rules })
4848
}

lib/cssom/CSSLayerBlockRule-impl.js

+12-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
const { implementation: CSSGroupingRuleImpl } = require('./CSSGroupingRule-impl')
3+
const CSSNestedDeclarations = require('./CSSNestedDeclarations.js')
34
const CSSRuleList = require('./CSSRuleList.js')
4-
const CSSStyleRule = require('./CSSStyleRule.js')
55
const { parseBlockContents } = require('../parse/parser')
66
const { serializeCSSComponentValue } = require('../serialize')
77

@@ -17,16 +17,19 @@ class CSSLayerBlockRuleImpl extends CSSGroupingRuleImpl {
1717
*/
1818
constructor(globalObject, args, privateData) {
1919
super(globalObject, args, privateData)
20+
const { parentStyleSheet } = this
2021
const { prelude, value } = privateData
21-
const { declarations, rules } = parseBlockContents(value, this)
22+
const rules = parseBlockContents(value, this).map(rule => {
23+
if (Array.isArray(rule) ) {
24+
return CSSNestedDeclarations.createImpl(globalObject, undefined, {
25+
declarations: rule,
26+
parentRule: this,
27+
parentStyleSheet,
28+
})
29+
}
30+
return rule
31+
})
2232
this.name = serializeCSSComponentValue(prelude)
23-
// Wrap declarations in nested style rule
24-
if (0 < declarations.length) {
25-
const data = { parentRule: this, parentStyleSheet: this.parentStyleSheet }
26-
const styleRule = CSSStyleRule.createImpl(globalObject, undefined, data)
27-
styleRule.style._declarations.push(...declarations)
28-
rules.unshift(styleRule)
29-
}
3033
this.cssRules = CSSRuleList.createImpl(globalObject, undefined, { rules })
3134
}
3235

lib/cssom/CSSMarginRule-impl.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class CSSMarginRuleImpl extends CSSRuleImpl {
1616
constructor(globalObject, args, privateData) {
1717
super(globalObject, args, privateData)
1818
const { name, value } = privateData
19-
const { declarations } = parseBlockContents(value, this)
19+
const [declarations = []] = parseBlockContents(value, this)
2020
this.name = name
2121
this.style = CSSMarginDescriptors.createImpl(globalObject, undefined, { declarations, parentRule: this })
2222
}
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
const { implementation: CSSRuleImpl } = require('./CSSRule-impl.js')
3+
const CSSStyleDeclaration = require('./CSSStyleDeclaration.js')
4+
5+
/**
6+
* @see {@link https://drafts.csswg.org/css-nesting-1/#cssnesteddeclarations}
7+
*/
8+
class CSSNestedDeclarationsImpl extends CSSRuleImpl {
9+
10+
/**
11+
* @param {DocumentOrShadowRoot} globalObject
12+
* @param {*[]} args
13+
* @param {object} privateData
14+
*/
15+
constructor(globalObject, args, privateData) {
16+
super(globalObject, args, privateData)
17+
// https://github.com/w3c/csswg-drafts/issues/11272
18+
this.style = CSSStyleDeclaration.create(globalObject, undefined, {
19+
declarations: privateData.declarations,
20+
parentRule: this,
21+
})
22+
}
23+
24+
/**
25+
* @returns {string}
26+
* @see {@link https://drafts.csswg.org/cssom-1/#dom-cssrule-csstext}
27+
*/
28+
get cssText() {
29+
return this.style.cssText
30+
}
31+
}
32+
33+
module.exports = {
34+
implementation: CSSNestedDeclarationsImpl,
35+
}
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
// https://drafts.csswg.org/css-nesting-1/#cssnesteddeclarations
3+
[Exposed=Window]
4+
interface CSSNestedDeclarations : CSSRule {
5+
// https://github.com/w3c/csswg-drafts/issues/11272
6+
//[SameObject, PutForwards=cssText] readonly attribute CSSStyleProperties style;
7+
[SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style;
8+
};

lib/cssom/CSSPageRule-impl.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
const { parseBlockContents, parseCSSGrammar } = require('../parse/parser.js')
33
const { implementation: CSSGroupingRuleImpl } = require('./CSSGroupingRule-impl')
4+
const CSSNestedDeclarations = require('./CSSNestedDeclarations.js')
45
const CSSPageDescriptors = require('./CSSPageDescriptors.js')
56
const CSSRuleList = require('./CSSRuleList.js')
67
const { serializeCSSComponentValue } = require('../serialize.js')
@@ -17,8 +18,20 @@ class CSSPageRuleImpl extends CSSGroupingRuleImpl {
1718
*/
1819
constructor(globalObject, args, privateData) {
1920
super(globalObject, args, privateData)
21+
const { parentStyleSheet } = this
2022
const { prelude, value } = privateData
21-
const { declarations, rules } = parseBlockContents(value, this)
23+
const contents = parseBlockContents(value, this)
24+
const declarations = Array.isArray(contents[0]) ? contents.shift() : []
25+
const rules = contents.map(rule => {
26+
if (Array.isArray(rule) ) {
27+
return CSSNestedDeclarations.createImpl(globalObject, undefined, {
28+
declarations: rule,
29+
parentRule: this,
30+
parentStyleSheet,
31+
})
32+
}
33+
return rule
34+
})
2235
this._selectors = prelude
2336
this.style = CSSPageDescriptors.createImpl(globalObject, undefined, { declarations, parentRule: this })
2437
this.cssRules = CSSRuleList.createImpl(globalObject, undefined, { rules })

lib/cssom/CSSPositionTryRule-impl.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class CSSPositionTryRuleImpl extends CSSRuleImpl {
1717
constructor(globalObject, args, privateData) {
1818
super(globalObject, args, privateData)
1919
const { prelude, value } = privateData
20-
const { declarations } = parseBlockContents(value, this)
20+
const [declarations = []] = parseBlockContents(value, this)
2121
this.name = serializeCSSComponentValue(prelude)
2222
this.style = CSSPositionTryDescriptors.createImpl(globalObject, undefined, { declarations, parentRule: this })
2323
}

lib/cssom/CSSPropertyRule-impl.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class CSSPropertyRuleImpl extends CSSRuleImpl {
1717
constructor(globalObject, args, privateData) {
1818
super(globalObject, args, privateData)
1919
const { prelude, value } = privateData
20-
const { declarations } = parseBlockContents(value, this)
20+
const [declarations = []] = parseBlockContents(value, this)
2121
this.name = serializeCSSComponentValue(prelude)
2222
declarations.forEach(({ name, value }) => {
2323
let attribute = cssPropertyToIDLAttribute(name)

lib/cssom/CSSScopeRule-impl.js

+13-25
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,11 @@
11

2-
const { list, omitted } = require('../values/value.js')
32
const { implementation: CSSGroupingRuleImpl } = require('./CSSGroupingRule-impl.js')
3+
const CSSNestedDeclarations = require('./CSSNestedDeclarations.js')
44
const CSSRuleList = require('./CSSRuleList.js')
5-
const CSSStyleRule = require('./CSSStyleRule.js')
65
const { isOmitted } = require('../utils/value.js')
76
const { parseBlockContents } = require('../parse/parser.js')
87
const { serializeCSSComponentValueList } = require('../serialize.js')
98

10-
const colon = { types: ['<delimiter-token>'], value: ':' }
11-
const scope = { types: ['<ident-token>'], value: 'scope' }
12-
const subclassSelector = list([colon, scope], '', ['<pseudo-class-selector>', '<subclass-selector>'])
13-
const subclassSelectorList = list([subclassSelector], '')
14-
const compound = list([omitted, subclassSelectorList], '', ['<compound-selector>'])
15-
const complexUnit = list([compound, list([], '')], '', ['<complex-selector-unit>'])
16-
const complexSelector = list([complexUnit, list()], ' ', ['<complex-selector>'])
17-
const relativeSelector = list([omitted, complexSelector], ' ', ['<relative-selector>'])
18-
const relativeSelectorList = list([relativeSelector], ',', ['<relative-selector-list>'])
19-
209
/**
2110
* @see {@link https://drafts.csswg.org/css-cascade-6/#cssscoperule}
2211
*/
@@ -29,8 +18,19 @@ class CSSScopeRuleImpl extends CSSGroupingRuleImpl {
2918
*/
3019
constructor(globalObject, args, privateData) {
3120
super(globalObject, args, privateData)
21+
const { parentStyleSheet } = this
3222
const { prelude, value } = privateData
33-
const { declarations, rules } = parseBlockContents(value, this)
23+
const rules = parseBlockContents(value, this).map(rule => {
24+
if (Array.isArray(rule) ) {
25+
return CSSNestedDeclarations.createImpl(globalObject, undefined, {
26+
declarations: rule,
27+
parentRule: this,
28+
parentStyleSheet,
29+
})
30+
}
31+
return rule
32+
})
33+
this.cssRules = CSSRuleList.createImpl(globalObject, undefined, { rules })
3434
if (isOmitted(prelude)) {
3535
this.start = null
3636
this.end = null
@@ -39,18 +39,6 @@ class CSSScopeRuleImpl extends CSSGroupingRuleImpl {
3939
this.start = isOmitted(start) ? null : serializeCSSComponentValueList(start.value)
4040
this.end = isOmitted(end) ? null : serializeCSSComponentValueList(end[1].value)
4141
}
42-
// Wrap declarations in nested style rule
43-
if (0 < declarations.length) {
44-
const data = {
45-
parentRule: this,
46-
parentStyleSheet: this.parentStyleSheet,
47-
prelude: relativeSelectorList,
48-
}
49-
const styleRule = CSSStyleRule.createImpl(globalObject, undefined, data)
50-
styleRule.style._declarations.push(...declarations)
51-
rules.unshift(styleRule)
52-
}
53-
this.cssRules = CSSRuleList.createImpl(globalObject, undefined, { rules })
5442
}
5543

5644
/**

lib/cssom/CSSStartingStyleRule-impl.js

+12-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
const { implementation: CSSGroupingRuleImpl } = require('./CSSGroupingRule-impl')
3+
const CSSNestedDeclarations = require('./CSSNestedDeclarations.js')
34
const CSSRuleList = require('./CSSRuleList.js')
4-
const CSSStyleRule = require('./CSSStyleRule.js')
55
const { parseBlockContents } = require('../parse/parser.js')
66

77
/**
@@ -16,14 +16,17 @@ class CSSStartingStyleRuleImpl extends CSSGroupingRuleImpl {
1616
*/
1717
constructor(globalObject, args, privateData) {
1818
super(globalObject, args, privateData)
19-
const { declarations, rules } = parseBlockContents(privateData.value, this)
20-
// Wrap declarations in nested style rule
21-
if (0 < declarations.length) {
22-
const data = { parentRule: this, parentStyleSheet: this.parentStyleSheet }
23-
const styleRule = CSSStyleRule.createImpl(globalObject, undefined, data)
24-
styleRule.style._declarations.push(...declarations)
25-
rules.unshift(styleRule)
26-
}
19+
const { parentStyleSheet } = this
20+
const rules = parseBlockContents(privateData.value, this).map(rule => {
21+
if (Array.isArray(rule) ) {
22+
return CSSNestedDeclarations.createImpl(globalObject, undefined, {
23+
declarations: rule,
24+
parentRule: this,
25+
parentStyleSheet,
26+
})
27+
}
28+
return rule
29+
})
2730
this.cssRules = CSSRuleList.createImpl(globalObject, undefined, { rules })
2831
}
2932

0 commit comments

Comments
 (0)