@acemir/cssom 0.9.29 → 0.9.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/CSSOM.js CHANGED
@@ -716,6 +716,122 @@ Object.defineProperty(CSSOM.CSSCounterStyleRule.prototype, "parse", {
716
716
 
717
717
 
718
718
 
719
+ /**
720
+ * @constructor
721
+ * @see https://drafts.css-houdini.org/css-properties-values-api/#the-css-property-rule-interface
722
+ */
723
+ CSSOM.CSSPropertyRule = function CSSPropertyRule() {
724
+ CSSOM.CSSRule.call(this);
725
+ this.__name = "";
726
+ this.__syntax = "";
727
+ this.__inherits = false;
728
+ this.__initialValue = null;
729
+ };
730
+
731
+ CSSOM.CSSPropertyRule.prototype = Object.create(CSSOM.CSSRule.prototype);
732
+ CSSOM.CSSPropertyRule.prototype.constructor = CSSOM.CSSPropertyRule;
733
+
734
+ Object.setPrototypeOf(CSSOM.CSSPropertyRule, CSSOM.CSSRule);
735
+
736
+ Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "type", {
737
+ value: 0,
738
+ writable: false
739
+ });
740
+
741
+ Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "cssText", {
742
+ get: function() {
743
+ var text = "@property " + this.name + " {";
744
+ if (this.syntax !== "") {
745
+ text += " syntax: \"" + this.syntax.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + "\";";
746
+ }
747
+ text += " inherits: " + (this.inherits ? "true" : "false") + ";";
748
+ if (this.initialValue !== null) {
749
+ text += " initial-value: " + this.initialValue + ";";
750
+ }
751
+ text += " }";
752
+ return text;
753
+ }
754
+ });
755
+
756
+ Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "name", {
757
+ get: function() {
758
+ return this.__name;
759
+ }
760
+ });
761
+
762
+ Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "syntax", {
763
+ get: function() {
764
+ return this.__syntax;
765
+ }
766
+ });
767
+
768
+ Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "inherits", {
769
+ get: function() {
770
+ return this.__inherits;
771
+ }
772
+ });
773
+
774
+ Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "initialValue", {
775
+ get: function() {
776
+ return this.__initialValue;
777
+ }
778
+ });
779
+
780
+ /**
781
+ * NON-STANDARD
782
+ * Rule text parser.
783
+ * @param {string} cssText
784
+ * @returns {boolean} True if the rule is valid and was parsed successfully
785
+ */
786
+ Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "parse", {
787
+ value: function(cssText) {
788
+ // Extract the name from "@property <name> { ... }"
789
+ var match = cssText.match(/@property\s+(--[^\s{]+)\s*\{([^]*)\}/);
790
+ if (!match) {
791
+ return false;
792
+ }
793
+
794
+ this.__name = match[1];
795
+ var bodyText = match[2];
796
+
797
+ // Parse syntax descriptor (REQUIRED)
798
+ var syntaxMatch = bodyText.match(/syntax\s*:\s*(['"])([^]*?)\1\s*;/);
799
+ if (!syntaxMatch) {
800
+ return false; // syntax is required
801
+ }
802
+ this.__syntax = syntaxMatch[2];
803
+
804
+ // Syntax cannot be empty
805
+ if (this.__syntax === "") {
806
+ return false;
807
+ }
808
+
809
+ // Parse inherits descriptor (REQUIRED)
810
+ var inheritsMatch = bodyText.match(/inherits\s*:\s*(true|false)\s*;/);
811
+ if (!inheritsMatch) {
812
+ return false; // inherits is required
813
+ }
814
+ this.__inherits = inheritsMatch[1] === "true";
815
+
816
+ // Parse initial-value descriptor (OPTIONAL, but required if syntax is not "*")
817
+ var initialValueMatch = bodyText.match(/initial-value\s*:\s*([^;]+);/);
818
+ if (initialValueMatch) {
819
+ this.__initialValue = initialValueMatch[1].trim();
820
+ } else {
821
+ // If syntax is not "*", initial-value is required
822
+ if (this.__syntax !== "*") {
823
+ return false;
824
+ }
825
+ }
826
+
827
+ return true; // Successfully parsed
828
+ }
829
+ });
830
+
831
+
832
+
833
+
834
+
719
835
  /**
720
836
  * @constructor
721
837
  * @see https://www.w3.org/TR/css-conditional-3/#the-cssconditionrule-interface
@@ -781,7 +897,8 @@ Object.defineProperty(CSSOM.CSSStyleRule.prototype, "selectorText", {
781
897
 
782
898
  this.__selectorText = trimmedValue;
783
899
  }
784
- }
900
+ },
901
+ configurable: true
785
902
  });
786
903
 
787
904
  Object.defineProperty(CSSOM.CSSStyleRule.prototype, "style", {
@@ -794,7 +911,8 @@ Object.defineProperty(CSSOM.CSSStyleRule.prototype, "style", {
794
911
  } else {
795
912
  this.__style = value;
796
913
  }
797
- }
914
+ },
915
+ configurable: true
798
916
  });
799
917
 
800
918
  Object.defineProperty(CSSOM.CSSStyleRule.prototype, "cssText", {
@@ -934,6 +1052,7 @@ Object.defineProperties(CSSOM.CSSMediaRule.prototype, {
934
1052
  this.__media = value;
935
1053
  }
936
1054
  },
1055
+ configurable: true,
937
1056
  enumerable: true
938
1057
  },
939
1058
  "conditionText": {
@@ -1728,6 +1847,11 @@ CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) {
1728
1847
 
1729
1848
  // Validate rule ordering based on CSS specification
1730
1849
  if (cssRule.constructor.name === 'CSSImportRule') {
1850
+ if (this.__constructed === true) {
1851
+ errorUtils.throwError(this, 'DOMException',
1852
+ "Failed to execute 'insertRule' on '" + this.constructor.name + "': Can't insert @import rules into a constructed stylesheet.",
1853
+ 'SyntaxError');
1854
+ }
1731
1855
  // @import rules cannot be inserted after @layer rules that already exist
1732
1856
  // They can only be inserted at the beginning or after other @import rules
1733
1857
  var firstLayerIndex = findFirstNonConstructorIndex(this.cssRules, ['CSSImportRule']);
@@ -3023,6 +3147,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
3023
3147
  "containerBlock": true,
3024
3148
  "conditionBlock": true,
3025
3149
  "counterStyleBlock": true,
3150
+ "propertyBlock": true,
3026
3151
  'documentRule-begin': true,
3027
3152
  "scopeBlock": true,
3028
3153
  "layerBlock": true,
@@ -3054,6 +3179,10 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
3054
3179
  if (ownerNodeMedia) {
3055
3180
  styleSheet.media.mediaText = ownerNodeMedia;
3056
3181
  }
3182
+ var ownerNodeTitle = opts.ownerNode.title || (opts.ownerNode.getAttribute && opts.ownerNode.getAttribute("title"));
3183
+ if (ownerNodeTitle) {
3184
+ styleSheet.__title = ownerNodeTitle;
3185
+ }
3057
3186
  }
3058
3187
 
3059
3188
  if (opts && opts.ownerRule) {
@@ -3069,7 +3198,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
3069
3198
  var ancestorRules = [];
3070
3199
  var prevScope;
3071
3200
 
3072
- var name, priority = "", styleRule, mediaRule, containerRule, counterStyleRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule, startingStyleRule, scopeRule, pageRule, layerBlockRule, layerStatementRule, nestedSelectorRule, namespaceRule;
3201
+ var name, priority = "", styleRule, mediaRule, containerRule, counterStyleRule, propertyRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule, startingStyleRule, scopeRule, pageRule, layerBlockRule, layerStatementRule, nestedSelectorRule, namespaceRule;
3073
3202
 
3074
3203
  // Track defined namespace prefixes for validation
3075
3204
  var definedNamespacePrefixes = {};
@@ -3384,6 +3513,44 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
3384
3513
  return invalidNestingPattern.test(selector);
3385
3514
  };
3386
3515
 
3516
+ /**
3517
+ * Checks if an at-rule can be nested based on parent chain validation.
3518
+ * Used for at-rules like `@counter-style`, `@property` and `@font-face` rules that can only be nested inside
3519
+ * `CSSScopeRule` or `CSSConditionRule` without `CSSStyleRule` in parent chain.
3520
+ * @returns {boolean} `true` if nesting is allowed, `false` otherwise
3521
+ */
3522
+ function canAtRuleBeNested() {
3523
+ if (currentScope === topScope) {
3524
+ return true; // Top-level is always allowed
3525
+ }
3526
+
3527
+ var hasStyleRuleInChain = false;
3528
+ var hasValidParent = false;
3529
+
3530
+ // Check currentScope
3531
+ if (currentScope.constructor.name === 'CSSStyleRule') {
3532
+ hasStyleRuleInChain = true;
3533
+ } else if (currentScope instanceof CSSOM.CSSScopeRule || currentScope instanceof CSSOM.CSSConditionRule) {
3534
+ hasValidParent = true;
3535
+ }
3536
+
3537
+ // Check ancestorRules for CSSStyleRule
3538
+ if (!hasStyleRuleInChain) {
3539
+ for (var j = 0; j < ancestorRules.length; j++) {
3540
+ if (ancestorRules[j].constructor.name === 'CSSStyleRule') {
3541
+ hasStyleRuleInChain = true;
3542
+ break;
3543
+ }
3544
+ if (ancestorRules[j] instanceof CSSOM.CSSScopeRule || ancestorRules[j] instanceof CSSOM.CSSConditionRule) {
3545
+ hasValidParent = true;
3546
+ }
3547
+ }
3548
+ }
3549
+
3550
+ // Allow nesting if we have a valid parent and no style rule in the chain
3551
+ return hasValidParent && !hasStyleRuleInChain;
3552
+ }
3553
+
3387
3554
  function validateAtRule(atRuleKey, validCallback, cannotBeNested) {
3388
3555
  var isValid = false;
3389
3556
  var sourceRuleRegExp = atRuleKey === "@import" ? forwardImportRuleValidationRegExp : forwardRuleValidationRegExp;
@@ -4565,13 +4732,28 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
4565
4732
  buffer = "";
4566
4733
  break;
4567
4734
  } else if (token.indexOf("@counter-style", i) === i) {
4735
+ buffer = "";
4736
+ // @counter-style can be nested only inside CSSScopeRule or CSSConditionRule
4737
+ // and only if there's no CSSStyleRule in the parent chain
4738
+ var cannotBeNested = !canAtRuleBeNested();
4568
4739
  validateAtRule("@counter-style", function () {
4569
4740
  state = "counterStyleBlock"
4570
4741
  counterStyleRule = new CSSOM.CSSCounterStyleRule();
4571
4742
  counterStyleRule.__starts = i;
4572
4743
  i += "counter-style".length;
4573
- }, true);
4744
+ }, cannotBeNested);
4745
+ break;
4746
+ } else if (token.indexOf("@property", i) === i) {
4574
4747
  buffer = "";
4748
+ // @property can be nested only inside CSSScopeRule or CSSConditionRule
4749
+ // and only if there's no CSSStyleRule in the parent chain
4750
+ var cannotBeNested = !canAtRuleBeNested();
4751
+ validateAtRule("@property", function () {
4752
+ state = "propertyBlock"
4753
+ propertyRule = new CSSOM.CSSPropertyRule();
4754
+ propertyRule.__starts = i;
4755
+ i += "property".length;
4756
+ }, cannotBeNested);
4575
4757
  break;
4576
4758
  } else if (token.indexOf("@scope", i) === i) {
4577
4759
  validateAtRule("@scope", function () {
@@ -4647,36 +4829,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
4647
4829
  buffer = "";
4648
4830
  // @font-face can be nested only inside CSSScopeRule or CSSConditionRule
4649
4831
  // and only if there's no CSSStyleRule in the parent chain
4650
- var cannotBeNested = true;
4651
- if (currentScope !== topScope) {
4652
- var hasStyleRuleInChain = false;
4653
- var hasValidParent = false;
4654
-
4655
- // Check currentScope
4656
- if (currentScope.constructor.name === 'CSSStyleRule') {
4657
- hasStyleRuleInChain = true;
4658
- } else if (currentScope instanceof CSSOM.CSSScopeRule || currentScope instanceof CSSOM.CSSConditionRule) {
4659
- hasValidParent = true;
4660
- }
4661
-
4662
- // Check ancestorRules for CSSStyleRule
4663
- if (!hasStyleRuleInChain) {
4664
- for (var j = 0; j < ancestorRules.length; j++) {
4665
- if (ancestorRules[j].constructor.name === 'CSSStyleRule') {
4666
- hasStyleRuleInChain = true;
4667
- break;
4668
- }
4669
- if (ancestorRules[j] instanceof CSSOM.CSSScopeRule || ancestorRules[j] instanceof CSSOM.CSSConditionRule) {
4670
- hasValidParent = true;
4671
- }
4672
- }
4673
- }
4674
-
4675
- // Allow nesting if we have a valid parent and no style rule in the chain
4676
- if (hasValidParent && !hasStyleRuleInChain) {
4677
- cannotBeNested = false;
4678
- }
4679
- }
4832
+ var cannotBeNested = !canAtRuleBeNested();
4680
4833
  validateAtRule("@font-face", function () {
4681
4834
  state = "fontFaceRule-begin";
4682
4835
  i += "font-face".length;
@@ -4793,8 +4946,25 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
4793
4946
 
4794
4947
  if (isValidCounterStyleName) {
4795
4948
  counterStyleRule.name = counterStyleName;
4796
- currentScope = parentRule = counterStyleRule;
4949
+ if (parentRule) {
4950
+ counterStyleRule.__parentRule = parentRule;
4951
+ }
4797
4952
  counterStyleRule.__parentStyleSheet = styleSheet;
4953
+ styleRule = counterStyleRule;
4954
+ }
4955
+ buffer = "";
4956
+ } else if (state === "propertyBlock") {
4957
+ var propertyName = buffer.trim().replace(/\n/g, "");
4958
+ // Validate: name must start with -- (custom property)
4959
+ var isValidPropertyName = propertyName.indexOf("--") === 0;
4960
+
4961
+ if (isValidPropertyName) {
4962
+ propertyRule.__name = propertyName;
4963
+ if (parentRule) {
4964
+ propertyRule.__parentRule = parentRule;
4965
+ }
4966
+ propertyRule.__parentStyleSheet = styleSheet;
4967
+ styleRule = propertyRule;
4798
4968
  }
4799
4969
  buffer = "";
4800
4970
  } else if (state === "conditionBlock") {
@@ -5082,6 +5252,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
5082
5252
  if (opts && opts.globalObject && opts.globalObject.CSSStyleSheet) {
5083
5253
  importRule.__styleSheet = new opts.globalObject.CSSStyleSheet();
5084
5254
  }
5255
+ importRule.styleSheet.__constructed = false;
5085
5256
  importRule.__parentStyleSheet = importRule.styleSheet.__parentStyleSheet = styleSheet;
5086
5257
  importRule.parse(buffer + character);
5087
5258
  topScope.cssRules.push(importRule);
@@ -5163,11 +5334,50 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
5163
5334
  if (state === "counterStyleBlock") {
5164
5335
  // FIXME : Implement missing properties on CSSCounterStyleRule interface and update parse method
5165
5336
  // For now it's just assigning entire rule text
5166
- counterStyleRule.parse("@counter-style " + counterStyleRule.name + " { " + buffer + " }");
5337
+ if (counterStyleRule.name) {
5338
+ // Only process if name was set (valid)
5339
+ counterStyleRule.parse("@counter-style " + counterStyleRule.name + " { " + buffer + " }");
5340
+ counterStyleRule.__ends = i + 1;
5341
+ // Add to parent's cssRules
5342
+ if (counterStyleRule.__parentRule) {
5343
+ counterStyleRule.__parentRule.cssRules.push(counterStyleRule);
5344
+ } else {
5345
+ topScope.cssRules.push(counterStyleRule);
5346
+ }
5347
+ }
5348
+ // Restore currentScope to parent after closing this rule
5349
+ if (counterStyleRule.__parentRule) {
5350
+ currentScope = counterStyleRule.__parentRule;
5351
+ }
5352
+ styleRule = null;
5167
5353
  buffer = "";
5168
5354
  state = "before-selector";
5355
+ break;
5356
+ }
5357
+ if (state === "propertyBlock") {
5358
+ // Only process if name was set (valid)
5359
+ if (propertyRule.__name) {
5360
+ var parseSuccess = propertyRule.parse("@property " + propertyRule.__name + " { " + buffer + " }");
5361
+ // Only add the rule if parse was successful (syntax, inherits, and initial-value validation passed)
5362
+ if (parseSuccess) {
5363
+ propertyRule.__ends = i + 1;
5364
+ // Add to parent's cssRules
5365
+ if (propertyRule.__parentRule) {
5366
+ propertyRule.__parentRule.cssRules.push(propertyRule);
5367
+ } else {
5368
+ topScope.cssRules.push(propertyRule);
5369
+ }
5370
+ }
5371
+ }
5372
+ // Restore currentScope to parent after closing this rule
5373
+ if (propertyRule.__parentRule) {
5374
+ currentScope = propertyRule.__parentRule;
5375
+ }
5376
+ styleRule = null;
5377
+ buffer = "";
5378
+ state = "before-selector";
5379
+ break;
5169
5380
  }
5170
-
5171
5381
  switch (state) {
5172
5382
  case "value":
5173
5383
  styleRule.style.setProperty(name, buffer.trim(), priority, parseError);
@@ -5292,6 +5502,8 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
5292
5502
  currentScope.cssRules.push(prevScope);
5293
5503
  }
5294
5504
  nestedSelectorRule = currentScope;
5505
+ // Stop here to preserve context for sibling selectors
5506
+ break;
5295
5507
  } else {
5296
5508
  // Top-level CSSStyleRule with nested grouping rule
5297
5509
  prevScope = currentScope;
@@ -42,6 +42,7 @@ Object.defineProperties(CSSOM.CSSMediaRule.prototype, {
42
42
  this.__media = value;
43
43
  }
44
44
  },
45
+ configurable: true,
45
46
  enumerable: true
46
47
  },
47
48
  "conditionText": {
@@ -0,0 +1,122 @@
1
+ //.CommonJS
2
+ var CSSOM = {
3
+ CSSRule: require("./CSSRule").CSSRule
4
+ };
5
+ ///CommonJS
6
+
7
+
8
+ /**
9
+ * @constructor
10
+ * @see https://drafts.css-houdini.org/css-properties-values-api/#the-css-property-rule-interface
11
+ */
12
+ CSSOM.CSSPropertyRule = function CSSPropertyRule() {
13
+ CSSOM.CSSRule.call(this);
14
+ this.__name = "";
15
+ this.__syntax = "";
16
+ this.__inherits = false;
17
+ this.__initialValue = null;
18
+ };
19
+
20
+ CSSOM.CSSPropertyRule.prototype = Object.create(CSSOM.CSSRule.prototype);
21
+ CSSOM.CSSPropertyRule.prototype.constructor = CSSOM.CSSPropertyRule;
22
+
23
+ Object.setPrototypeOf(CSSOM.CSSPropertyRule, CSSOM.CSSRule);
24
+
25
+ Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "type", {
26
+ value: 0,
27
+ writable: false
28
+ });
29
+
30
+ Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "cssText", {
31
+ get: function() {
32
+ var text = "@property " + this.name + " {";
33
+ if (this.syntax !== "") {
34
+ text += " syntax: \"" + this.syntax.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + "\";";
35
+ }
36
+ text += " inherits: " + (this.inherits ? "true" : "false") + ";";
37
+ if (this.initialValue !== null) {
38
+ text += " initial-value: " + this.initialValue + ";";
39
+ }
40
+ text += " }";
41
+ return text;
42
+ }
43
+ });
44
+
45
+ Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "name", {
46
+ get: function() {
47
+ return this.__name;
48
+ }
49
+ });
50
+
51
+ Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "syntax", {
52
+ get: function() {
53
+ return this.__syntax;
54
+ }
55
+ });
56
+
57
+ Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "inherits", {
58
+ get: function() {
59
+ return this.__inherits;
60
+ }
61
+ });
62
+
63
+ Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "initialValue", {
64
+ get: function() {
65
+ return this.__initialValue;
66
+ }
67
+ });
68
+
69
+ /**
70
+ * NON-STANDARD
71
+ * Rule text parser.
72
+ * @param {string} cssText
73
+ * @returns {boolean} True if the rule is valid and was parsed successfully
74
+ */
75
+ Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "parse", {
76
+ value: function(cssText) {
77
+ // Extract the name from "@property <name> { ... }"
78
+ var match = cssText.match(/@property\s+(--[^\s{]+)\s*\{([^]*)\}/);
79
+ if (!match) {
80
+ return false;
81
+ }
82
+
83
+ this.__name = match[1];
84
+ var bodyText = match[2];
85
+
86
+ // Parse syntax descriptor (REQUIRED)
87
+ var syntaxMatch = bodyText.match(/syntax\s*:\s*(['"])([^]*?)\1\s*;/);
88
+ if (!syntaxMatch) {
89
+ return false; // syntax is required
90
+ }
91
+ this.__syntax = syntaxMatch[2];
92
+
93
+ // Syntax cannot be empty
94
+ if (this.__syntax === "") {
95
+ return false;
96
+ }
97
+
98
+ // Parse inherits descriptor (REQUIRED)
99
+ var inheritsMatch = bodyText.match(/inherits\s*:\s*(true|false)\s*;/);
100
+ if (!inheritsMatch) {
101
+ return false; // inherits is required
102
+ }
103
+ this.__inherits = inheritsMatch[1] === "true";
104
+
105
+ // Parse initial-value descriptor (OPTIONAL, but required if syntax is not "*")
106
+ var initialValueMatch = bodyText.match(/initial-value\s*:\s*([^;]+);/);
107
+ if (initialValueMatch) {
108
+ this.__initialValue = initialValueMatch[1].trim();
109
+ } else {
110
+ // If syntax is not "*", initial-value is required
111
+ if (this.__syntax !== "*") {
112
+ return false;
113
+ }
114
+ }
115
+
116
+ return true; // Successfully parsed
117
+ }
118
+ });
119
+
120
+ //.CommonJS
121
+ exports.CSSPropertyRule = CSSOM.CSSPropertyRule;
122
+ ///CommonJS
@@ -55,7 +55,8 @@ Object.defineProperty(CSSOM.CSSStyleRule.prototype, "selectorText", {
55
55
 
56
56
  this.__selectorText = trimmedValue;
57
57
  }
58
- }
58
+ },
59
+ configurable: true
59
60
  });
60
61
 
61
62
  Object.defineProperty(CSSOM.CSSStyleRule.prototype, "style", {
@@ -68,7 +69,8 @@ Object.defineProperty(CSSOM.CSSStyleRule.prototype, "style", {
68
69
  } else {
69
70
  this.__style = value;
70
71
  }
71
- }
72
+ },
73
+ configurable: true
72
74
  });
73
75
 
74
76
  Object.defineProperty(CSSOM.CSSStyleRule.prototype, "cssText", {
@@ -123,6 +123,11 @@ CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) {
123
123
 
124
124
  // Validate rule ordering based on CSS specification
125
125
  if (cssRule.constructor.name === 'CSSImportRule') {
126
+ if (this.__constructed === true) {
127
+ errorUtils.throwError(this, 'DOMException',
128
+ "Failed to execute 'insertRule' on '" + this.constructor.name + "': Can't insert @import rules into a constructed stylesheet.",
129
+ 'SyntaxError');
130
+ }
126
131
  // @import rules cannot be inserted after @layer rules that already exist
127
132
  // They can only be inserted at the beginning or after other @import rules
128
133
  var firstLayerIndex = findFirstNonConstructorIndex(this.cssRules, ['CSSImportRule']);
package/lib/index.js CHANGED
@@ -13,6 +13,7 @@ exports.CSSRuleList = require('./CSSRuleList').CSSRuleList;
13
13
  exports.CSSNestedDeclarations = require('./CSSNestedDeclarations').CSSNestedDeclarations;
14
14
  exports.CSSGroupingRule = require('./CSSGroupingRule').CSSGroupingRule;
15
15
  exports.CSSCounterStyleRule = require('./CSSCounterStyleRule').CSSCounterStyleRule;
16
+ exports.CSSPropertyRule = require('./CSSPropertyRule').CSSPropertyRule;
16
17
  exports.CSSConditionRule = require('./CSSConditionRule').CSSConditionRule;
17
18
  exports.CSSStyleRule = require('./CSSStyleRule').CSSStyleRule;
18
19
  exports.MediaList = require('./MediaList').MediaList;
package/lib/parse.js CHANGED
@@ -52,6 +52,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
52
52
  "containerBlock": true,
53
53
  "conditionBlock": true,
54
54
  "counterStyleBlock": true,
55
+ "propertyBlock": true,
55
56
  'documentRule-begin': true,
56
57
  "scopeBlock": true,
57
58
  "layerBlock": true,
@@ -83,6 +84,10 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
83
84
  if (ownerNodeMedia) {
84
85
  styleSheet.media.mediaText = ownerNodeMedia;
85
86
  }
87
+ var ownerNodeTitle = opts.ownerNode.title || (opts.ownerNode.getAttribute && opts.ownerNode.getAttribute("title"));
88
+ if (ownerNodeTitle) {
89
+ styleSheet.__title = ownerNodeTitle;
90
+ }
86
91
  }
87
92
 
88
93
  if (opts && opts.ownerRule) {
@@ -98,7 +103,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
98
103
  var ancestorRules = [];
99
104
  var prevScope;
100
105
 
101
- var name, priority = "", styleRule, mediaRule, containerRule, counterStyleRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule, startingStyleRule, scopeRule, pageRule, layerBlockRule, layerStatementRule, nestedSelectorRule, namespaceRule;
106
+ var name, priority = "", styleRule, mediaRule, containerRule, counterStyleRule, propertyRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule, startingStyleRule, scopeRule, pageRule, layerBlockRule, layerStatementRule, nestedSelectorRule, namespaceRule;
102
107
 
103
108
  // Track defined namespace prefixes for validation
104
109
  var definedNamespacePrefixes = {};
@@ -413,6 +418,44 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
413
418
  return invalidNestingPattern.test(selector);
414
419
  };
415
420
 
421
+ /**
422
+ * Checks if an at-rule can be nested based on parent chain validation.
423
+ * Used for at-rules like `@counter-style`, `@property` and `@font-face` rules that can only be nested inside
424
+ * `CSSScopeRule` or `CSSConditionRule` without `CSSStyleRule` in parent chain.
425
+ * @returns {boolean} `true` if nesting is allowed, `false` otherwise
426
+ */
427
+ function canAtRuleBeNested() {
428
+ if (currentScope === topScope) {
429
+ return true; // Top-level is always allowed
430
+ }
431
+
432
+ var hasStyleRuleInChain = false;
433
+ var hasValidParent = false;
434
+
435
+ // Check currentScope
436
+ if (currentScope.constructor.name === 'CSSStyleRule') {
437
+ hasStyleRuleInChain = true;
438
+ } else if (currentScope instanceof CSSOM.CSSScopeRule || currentScope instanceof CSSOM.CSSConditionRule) {
439
+ hasValidParent = true;
440
+ }
441
+
442
+ // Check ancestorRules for CSSStyleRule
443
+ if (!hasStyleRuleInChain) {
444
+ for (var j = 0; j < ancestorRules.length; j++) {
445
+ if (ancestorRules[j].constructor.name === 'CSSStyleRule') {
446
+ hasStyleRuleInChain = true;
447
+ break;
448
+ }
449
+ if (ancestorRules[j] instanceof CSSOM.CSSScopeRule || ancestorRules[j] instanceof CSSOM.CSSConditionRule) {
450
+ hasValidParent = true;
451
+ }
452
+ }
453
+ }
454
+
455
+ // Allow nesting if we have a valid parent and no style rule in the chain
456
+ return hasValidParent && !hasStyleRuleInChain;
457
+ }
458
+
416
459
  function validateAtRule(atRuleKey, validCallback, cannotBeNested) {
417
460
  var isValid = false;
418
461
  var sourceRuleRegExp = atRuleKey === "@import" ? forwardImportRuleValidationRegExp : forwardRuleValidationRegExp;
@@ -1594,13 +1637,28 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
1594
1637
  buffer = "";
1595
1638
  break;
1596
1639
  } else if (token.indexOf("@counter-style", i) === i) {
1640
+ buffer = "";
1641
+ // @counter-style can be nested only inside CSSScopeRule or CSSConditionRule
1642
+ // and only if there's no CSSStyleRule in the parent chain
1643
+ var cannotBeNested = !canAtRuleBeNested();
1597
1644
  validateAtRule("@counter-style", function () {
1598
1645
  state = "counterStyleBlock"
1599
1646
  counterStyleRule = new CSSOM.CSSCounterStyleRule();
1600
1647
  counterStyleRule.__starts = i;
1601
1648
  i += "counter-style".length;
1602
- }, true);
1649
+ }, cannotBeNested);
1650
+ break;
1651
+ } else if (token.indexOf("@property", i) === i) {
1603
1652
  buffer = "";
1653
+ // @property can be nested only inside CSSScopeRule or CSSConditionRule
1654
+ // and only if there's no CSSStyleRule in the parent chain
1655
+ var cannotBeNested = !canAtRuleBeNested();
1656
+ validateAtRule("@property", function () {
1657
+ state = "propertyBlock"
1658
+ propertyRule = new CSSOM.CSSPropertyRule();
1659
+ propertyRule.__starts = i;
1660
+ i += "property".length;
1661
+ }, cannotBeNested);
1604
1662
  break;
1605
1663
  } else if (token.indexOf("@scope", i) === i) {
1606
1664
  validateAtRule("@scope", function () {
@@ -1676,36 +1734,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
1676
1734
  buffer = "";
1677
1735
  // @font-face can be nested only inside CSSScopeRule or CSSConditionRule
1678
1736
  // and only if there's no CSSStyleRule in the parent chain
1679
- var cannotBeNested = true;
1680
- if (currentScope !== topScope) {
1681
- var hasStyleRuleInChain = false;
1682
- var hasValidParent = false;
1683
-
1684
- // Check currentScope
1685
- if (currentScope.constructor.name === 'CSSStyleRule') {
1686
- hasStyleRuleInChain = true;
1687
- } else if (currentScope instanceof CSSOM.CSSScopeRule || currentScope instanceof CSSOM.CSSConditionRule) {
1688
- hasValidParent = true;
1689
- }
1690
-
1691
- // Check ancestorRules for CSSStyleRule
1692
- if (!hasStyleRuleInChain) {
1693
- for (var j = 0; j < ancestorRules.length; j++) {
1694
- if (ancestorRules[j].constructor.name === 'CSSStyleRule') {
1695
- hasStyleRuleInChain = true;
1696
- break;
1697
- }
1698
- if (ancestorRules[j] instanceof CSSOM.CSSScopeRule || ancestorRules[j] instanceof CSSOM.CSSConditionRule) {
1699
- hasValidParent = true;
1700
- }
1701
- }
1702
- }
1703
-
1704
- // Allow nesting if we have a valid parent and no style rule in the chain
1705
- if (hasValidParent && !hasStyleRuleInChain) {
1706
- cannotBeNested = false;
1707
- }
1708
- }
1737
+ var cannotBeNested = !canAtRuleBeNested();
1709
1738
  validateAtRule("@font-face", function () {
1710
1739
  state = "fontFaceRule-begin";
1711
1740
  i += "font-face".length;
@@ -1822,8 +1851,25 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
1822
1851
 
1823
1852
  if (isValidCounterStyleName) {
1824
1853
  counterStyleRule.name = counterStyleName;
1825
- currentScope = parentRule = counterStyleRule;
1854
+ if (parentRule) {
1855
+ counterStyleRule.__parentRule = parentRule;
1856
+ }
1826
1857
  counterStyleRule.__parentStyleSheet = styleSheet;
1858
+ styleRule = counterStyleRule;
1859
+ }
1860
+ buffer = "";
1861
+ } else if (state === "propertyBlock") {
1862
+ var propertyName = buffer.trim().replace(/\n/g, "");
1863
+ // Validate: name must start with -- (custom property)
1864
+ var isValidPropertyName = propertyName.indexOf("--") === 0;
1865
+
1866
+ if (isValidPropertyName) {
1867
+ propertyRule.__name = propertyName;
1868
+ if (parentRule) {
1869
+ propertyRule.__parentRule = parentRule;
1870
+ }
1871
+ propertyRule.__parentStyleSheet = styleSheet;
1872
+ styleRule = propertyRule;
1827
1873
  }
1828
1874
  buffer = "";
1829
1875
  } else if (state === "conditionBlock") {
@@ -2111,6 +2157,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
2111
2157
  if (opts && opts.globalObject && opts.globalObject.CSSStyleSheet) {
2112
2158
  importRule.__styleSheet = new opts.globalObject.CSSStyleSheet();
2113
2159
  }
2160
+ importRule.styleSheet.__constructed = false;
2114
2161
  importRule.__parentStyleSheet = importRule.styleSheet.__parentStyleSheet = styleSheet;
2115
2162
  importRule.parse(buffer + character);
2116
2163
  topScope.cssRules.push(importRule);
@@ -2192,11 +2239,50 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
2192
2239
  if (state === "counterStyleBlock") {
2193
2240
  // FIXME : Implement missing properties on CSSCounterStyleRule interface and update parse method
2194
2241
  // For now it's just assigning entire rule text
2195
- counterStyleRule.parse("@counter-style " + counterStyleRule.name + " { " + buffer + " }");
2242
+ if (counterStyleRule.name) {
2243
+ // Only process if name was set (valid)
2244
+ counterStyleRule.parse("@counter-style " + counterStyleRule.name + " { " + buffer + " }");
2245
+ counterStyleRule.__ends = i + 1;
2246
+ // Add to parent's cssRules
2247
+ if (counterStyleRule.__parentRule) {
2248
+ counterStyleRule.__parentRule.cssRules.push(counterStyleRule);
2249
+ } else {
2250
+ topScope.cssRules.push(counterStyleRule);
2251
+ }
2252
+ }
2253
+ // Restore currentScope to parent after closing this rule
2254
+ if (counterStyleRule.__parentRule) {
2255
+ currentScope = counterStyleRule.__parentRule;
2256
+ }
2257
+ styleRule = null;
2196
2258
  buffer = "";
2197
2259
  state = "before-selector";
2260
+ break;
2261
+ }
2262
+ if (state === "propertyBlock") {
2263
+ // Only process if name was set (valid)
2264
+ if (propertyRule.__name) {
2265
+ var parseSuccess = propertyRule.parse("@property " + propertyRule.__name + " { " + buffer + " }");
2266
+ // Only add the rule if parse was successful (syntax, inherits, and initial-value validation passed)
2267
+ if (parseSuccess) {
2268
+ propertyRule.__ends = i + 1;
2269
+ // Add to parent's cssRules
2270
+ if (propertyRule.__parentRule) {
2271
+ propertyRule.__parentRule.cssRules.push(propertyRule);
2272
+ } else {
2273
+ topScope.cssRules.push(propertyRule);
2274
+ }
2275
+ }
2276
+ }
2277
+ // Restore currentScope to parent after closing this rule
2278
+ if (propertyRule.__parentRule) {
2279
+ currentScope = propertyRule.__parentRule;
2280
+ }
2281
+ styleRule = null;
2282
+ buffer = "";
2283
+ state = "before-selector";
2284
+ break;
2198
2285
  }
2199
-
2200
2286
  switch (state) {
2201
2287
  case "value":
2202
2288
  styleRule.style.setProperty(name, buffer.trim(), priority, parseError);
@@ -2321,6 +2407,8 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
2321
2407
  currentScope.cssRules.push(prevScope);
2322
2408
  }
2323
2409
  nestedSelectorRule = currentScope;
2410
+ // Stop here to preserve context for sibling selectors
2411
+ break;
2324
2412
  } else {
2325
2413
  // Top-level CSSStyleRule with nested grouping rule
2326
2414
  prevScope = currentScope;
@@ -2484,6 +2572,7 @@ CSSOM.CSSNamespaceRule = require("./CSSNamespaceRule").CSSNamespaceRule;
2484
2572
  CSSOM.CSSGroupingRule = require("./CSSGroupingRule").CSSGroupingRule;
2485
2573
  CSSOM.CSSMediaRule = require("./CSSMediaRule").CSSMediaRule;
2486
2574
  CSSOM.CSSCounterStyleRule = require("./CSSCounterStyleRule").CSSCounterStyleRule;
2575
+ CSSOM.CSSPropertyRule = require("./CSSPropertyRule").CSSPropertyRule;
2487
2576
  CSSOM.CSSContainerRule = require("./CSSContainerRule").CSSContainerRule;
2488
2577
  CSSOM.CSSConditionRule = require("./CSSConditionRule").CSSConditionRule;
2489
2578
  CSSOM.CSSSupportsRule = require("./CSSSupportsRule").CSSSupportsRule;
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "parser",
8
8
  "styleSheet"
9
9
  ],
10
- "version": "0.9.29",
10
+ "version": "0.9.30",
11
11
  "author": "Nikita Vasilyev <me@elv1s.ru>",
12
12
  "contributors": [
13
13
  "Acemir Sousa Mendes <acemirsm@gmail.com>"
@@ -24,7 +24,8 @@
24
24
  "release": "npm run build && changeset publish"
25
25
  },
26
26
  "devDependencies": {
27
- "@changesets/changelog-github": "^0.5.1",
28
- "@changesets/cli": "^2.27.1"
27
+ "@changesets/changelog-github": "^0.5.2",
28
+ "@changesets/cli": "^2.29.8",
29
+ "@changesets/get-release-plan": "^4.0.14"
29
30
  }
30
31
  }