@acemir/cssom 0.9.16 → 0.9.18

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
@@ -1,6 +1,112 @@
1
1
  var CSSOM = {};
2
2
 
3
3
 
4
+ // Utility functions for CSSOM error handling
5
+
6
+ /**
7
+ * Gets the appropriate error constructor from the global object context.
8
+ * Tries to find the error constructor from parentStyleSheet.__globalObject,
9
+ * then from __globalObject, then falls back to the native constructor.
10
+ *
11
+ * @param {Object} context - The CSSOM object (rule, stylesheet, etc.)
12
+ * @param {string} errorType - The error type ('TypeError', 'RangeError', 'DOMException', etc.)
13
+ * @return {Function} The error constructor
14
+ */
15
+ function getErrorConstructor(context, errorType) {
16
+ // Try parentStyleSheet.__globalObject first
17
+ if (context.parentStyleSheet && context.parentStyleSheet.__globalObject && context.parentStyleSheet.__globalObject[errorType]) {
18
+ return context.parentStyleSheet.__globalObject[errorType];
19
+ }
20
+
21
+ // Try __parentStyleSheet (alternative naming)
22
+ if (context.__parentStyleSheet && context.__parentStyleSheet.__globalObject && context.__parentStyleSheet.__globalObject[errorType]) {
23
+ return context.__parentStyleSheet.__globalObject[errorType];
24
+ }
25
+
26
+ // Try __globalObject on the context itself
27
+ if (context.__globalObject && context.__globalObject[errorType]) {
28
+ return context.__globalObject[errorType];
29
+ }
30
+
31
+ // Fall back to native constructor
32
+ return (typeof global !== 'undefined' && global[errorType]) ||
33
+ (typeof window !== 'undefined' && window[errorType]) ||
34
+ eval(errorType);
35
+ }
36
+
37
+ /**
38
+ * Creates and throws an appropriate error with context-aware constructor.
39
+ *
40
+ * @param {Object} context - The CSSOM object (rule, stylesheet, etc.)
41
+ * @param {string} errorType - The error type ('TypeError', 'RangeError', 'DOMException', etc.)
42
+ * @param {string} message - The error message
43
+ * @param {string} [name] - Optional name for DOMException
44
+ */
45
+ function throwError(context, errorType, message, name) {
46
+ var ErrorConstructor = getErrorConstructor(context, errorType);
47
+ var error = new ErrorConstructor(message, name);
48
+ throw error;
49
+ }
50
+
51
+ /**
52
+ * Throws a TypeError for missing required arguments.
53
+ *
54
+ * @param {Object} context - The CSSOM object
55
+ * @param {string} methodName - The method name (e.g., 'appendRule')
56
+ * @param {string} objectName - The object name (e.g., 'CSSKeyframesRule')
57
+ * @param {number} [required=1] - Number of required arguments
58
+ * @param {number} [provided=0] - Number of provided arguments
59
+ */
60
+ function throwMissingArguments(context, methodName, objectName, required, provided) {
61
+ required = required || 1;
62
+ provided = provided || 0;
63
+ var message = "Failed to execute '" + methodName + "' on '" + objectName + "': " +
64
+ required + " argument" + (required > 1 ? "s" : "") + " required, but only " +
65
+ provided + " present.";
66
+ throwError(context, 'TypeError', message);
67
+ }
68
+
69
+ /**
70
+ * Throws a DOMException for parse errors.
71
+ *
72
+ * @param {Object} context - The CSSOM object
73
+ * @param {string} methodName - The method name
74
+ * @param {string} objectName - The object name
75
+ * @param {string} rule - The rule that failed to parse
76
+ * @param {string} [name='SyntaxError'] - The DOMException name
77
+ */
78
+ function throwParseError(context, methodName, objectName, rule, name) {
79
+ var message = "Failed to execute '" + methodName + "' on '" + objectName + "': " +
80
+ "Failed to parse the rule '" + rule + "'.";
81
+ throwError(context, 'DOMException', message, name || 'SyntaxError');
82
+ }
83
+
84
+ /**
85
+ * Throws a DOMException for index errors.
86
+ *
87
+ * @param {Object} context - The CSSOM object
88
+ * @param {string} methodName - The method name
89
+ * @param {string} objectName - The object name
90
+ * @param {number} index - The invalid index
91
+ * @param {number} maxIndex - The maximum valid index
92
+ * @param {string} [name='IndexSizeError'] - The DOMException name
93
+ */
94
+ function throwIndexError(context, methodName, objectName, index, maxIndex, name) {
95
+ var message = "Failed to execute '" + methodName + "' on '" + objectName + "': " +
96
+ "The index provided (" + index + ") is larger than the maximum index (" + maxIndex + ").";
97
+ throwError(context, 'DOMException', message, name || 'IndexSizeError');
98
+ }
99
+
100
+ var errorUtils = {
101
+ getErrorConstructor: getErrorConstructor,
102
+ throwError: throwError,
103
+ throwMissingArguments: throwMissingArguments,
104
+ throwParseError: throwParseError,
105
+ throwIndexError: throwIndexError
106
+ };
107
+
108
+
109
+
4
110
  // NOTE: Check viability to add a validation for css values or use a dependency like csstree-validator
5
111
  /**
6
112
  * Regular expression to detect invalid characters in the value portion of a CSS style declaration.
@@ -179,58 +285,91 @@ CSSOM.CSSStyleDeclaration.prototype = {
179
285
 
180
286
 
181
287
 
288
+ try {
289
+ CSSOM.CSSStyleDeclaration = require("cssstyle").CSSStyleDeclaration;
290
+ } catch (e) {
291
+ // ignore
292
+ }
293
+
182
294
  /**
183
295
  * @constructor
184
296
  * @see http://dev.w3.org/csswg/cssom/#the-cssrule-interface
185
297
  * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule
186
298
  */
187
299
  CSSOM.CSSRule = function CSSRule() {
188
- this.parentRule = null;
189
- this.parentStyleSheet = null;
300
+ this.__parentRule = null;
301
+ this.__parentStyleSheet = null;
190
302
  };
191
303
 
192
- CSSOM.CSSRule.UNKNOWN_RULE = 0; // obsolete
193
- CSSOM.CSSRule.STYLE_RULE = 1;
194
- CSSOM.CSSRule.CHARSET_RULE = 2; // obsolete
195
- CSSOM.CSSRule.IMPORT_RULE = 3;
196
- CSSOM.CSSRule.MEDIA_RULE = 4;
197
- CSSOM.CSSRule.FONT_FACE_RULE = 5;
198
- CSSOM.CSSRule.PAGE_RULE = 6;
199
- CSSOM.CSSRule.KEYFRAMES_RULE = 7;
200
- CSSOM.CSSRule.KEYFRAME_RULE = 8;
201
- CSSOM.CSSRule.MARGIN_RULE = 9;
202
- CSSOM.CSSRule.NAMESPACE_RULE = 10;
203
- CSSOM.CSSRule.COUNTER_STYLE_RULE = 11;
204
- CSSOM.CSSRule.SUPPORTS_RULE = 12;
205
- CSSOM.CSSRule.DOCUMENT_RULE = 13;
206
- CSSOM.CSSRule.FONT_FEATURE_VALUES_RULE = 14;
207
- CSSOM.CSSRule.VIEWPORT_RULE = 15;
208
- CSSOM.CSSRule.REGION_STYLE_RULE = 16;
209
- CSSOM.CSSRule.CONTAINER_RULE = 17;
210
- CSSOM.CSSRule.LAYER_BLOCK_RULE = 18;
211
- CSSOM.CSSRule.STARTING_STYLE_RULE = 1002;
212
-
213
- CSSOM.CSSRule.prototype = {
214
- constructor: CSSOM.CSSRule,
215
- //FIXME
216
- };
304
+ Object.defineProperties(CSSOM.CSSRule.prototype, {
305
+
306
+ constructor: { value: CSSOM.CSSRule },
307
+
308
+ parentRule: {
309
+ get: function() {
310
+ return this.__parentRule
311
+ }
312
+ },
313
+
314
+ parentStyleSheet: {
315
+ get: function() {
316
+ return this.__parentStyleSheet
317
+ }
318
+ },
319
+
320
+ UNKNOWN_RULE: { value: 0, enumerable: true }, // obsolet
321
+ STYLE_RULE: { value: 1, enumerable: true },
322
+ CHARSET_RULE: { value: 2, enumerable: true }, // obsolet
323
+ IMPORT_RULE: { value: 3, enumerable: true },
324
+ MEDIA_RULE: { value: 4, enumerable: true },
325
+ FONT_FACE_RULE: { value: 5, enumerable: true },
326
+ PAGE_RULE: { value: 6, enumerable: true },
327
+ KEYFRAMES_RULE: { value: 7, enumerable: true },
328
+ KEYFRAME_RULE: { value: 8, enumerable: true },
329
+ MARGIN_RULE: { value: 9, enumerable: true },
330
+ NAMESPACE_RULE: { value: 10, enumerable: true },
331
+ COUNTER_STYLE_RULE: { value: 11, enumerable: true },
332
+ SUPPORTS_RULE: { value: 12, enumerable: true },
333
+ DOCUMENT_RULE: { value: 13, enumerable: true },
334
+ FONT_FEATURE_VALUES_RULE: { value: 14, enumerable: true },
335
+ VIEWPORT_RULE: { value: 15, enumerable: true },
336
+ REGION_STYLE_RULE: { value: 16, enumerable: true },
337
+ CONTAINER_RULE: { value: 17, enumerable: true },
338
+ LAYER_BLOCK_RULE: { value: 18, enumerable: true },
339
+ STARTING_STYLE_RULE: { value: 1002, enumerable: true },
340
+ })
341
+
342
+
343
+
344
+
217
345
 
218
- exports.CSSRule = CSSOM.CSSRule;
219
- ///CommonJS
220
346
  /**
221
347
  * @constructor
222
348
  * @see https://drafts.csswg.org/css-nesting-1/
223
349
  */
224
350
  CSSOM.CSSNestedDeclarations = function CSSNestedDeclarations() {
225
351
  CSSOM.CSSRule.call(this);
226
- this.style = new CSSOM.CSSStyleDeclaration();
227
- this.style.parentRule = this;
352
+ this.__style = new CSSOM.CSSStyleDeclaration();
353
+ this.__style.parentRule = this;
228
354
  };
229
355
 
230
356
  CSSOM.CSSNestedDeclarations.prototype = new CSSOM.CSSRule();
231
357
  CSSOM.CSSNestedDeclarations.prototype.constructor = CSSOM.CSSNestedDeclarations;
232
358
  CSSOM.CSSNestedDeclarations.prototype.type = 0;
233
359
 
360
+ Object.defineProperty(CSSOM.CSSNestedDeclarations.prototype, "style", {
361
+ get: function() {
362
+ return this.__style;
363
+ },
364
+ set: function(value) {
365
+ if (typeof value === "string") {
366
+ this.__style.cssText = value;
367
+ } else {
368
+ this.__style = value;
369
+ }
370
+ }
371
+ });
372
+
234
373
  Object.defineProperty(CSSOM.CSSNestedDeclarations.prototype, "cssText", {
235
374
  get: function () {
236
375
  return this.style.cssText;
@@ -239,6 +378,9 @@ Object.defineProperty(CSSOM.CSSNestedDeclarations.prototype, "cssText", {
239
378
  enumerable: true,
240
379
  });
241
380
 
381
+
382
+
383
+
242
384
  /**
243
385
  * @constructor
244
386
  * @see https://drafts.csswg.org/cssom/#the-cssgroupingrule-interface
@@ -269,11 +411,42 @@ CSSOM.CSSGroupingRule.prototype.constructor = CSSOM.CSSGroupingRule;
269
411
  * @return {number} The index within the grouping rule's collection of the newly inserted rule.
270
412
  */
271
413
  CSSOM.CSSGroupingRule.prototype.insertRule = function insertRule(rule, index) {
272
- if (index < 0 || index > this.cssRules.length) {
273
- throw new RangeError("INDEX_SIZE_ERR");
414
+ if (rule === undefined && index === undefined) {
415
+ errorUtils.throwMissingArguments(this, 'insertRule', this.constructor.name);
416
+ }
417
+ if (index === void 0) {
418
+ index = 0;
274
419
  }
275
- var cssRule = CSSOM.parse(rule).cssRules[0];
276
- cssRule.parentRule = this;
420
+ index = Number(index);
421
+ if (index < 0) {
422
+ index = 4294967296 + index;
423
+ }
424
+ if (index > this.cssRules.length) {
425
+ errorUtils.throwIndexError(this, 'insertRule', this.constructor.name, index, this.cssRules.length);
426
+ }
427
+
428
+ var ruleToParse = String(rule);
429
+ var parsedSheet = CSSOM.parse(ruleToParse);
430
+ if (parsedSheet.cssRules.length !== 1) {
431
+ errorUtils.throwParseError(this, 'insertRule', this.constructor.name, ruleToParse, 'SyntaxError');
432
+ }
433
+ var cssRule = parsedSheet.cssRules[0];
434
+
435
+ // Check for rules that cannot be inserted inside a CSSGroupingRule
436
+ if (cssRule.constructor.name === 'CSSImportRule' || cssRule.constructor.name === 'CSSNamespaceRule') {
437
+ var ruleKeyword = cssRule.constructor.name === 'CSSImportRule' ? '@import' : '@namespace';
438
+ errorUtils.throwError(this, 'DOMException',
439
+ "Failed to execute 'insertRule' on '" + this.constructor.name + "': " +
440
+ "'" + ruleKeyword + "' rules cannot be inserted inside a group rule.",
441
+ 'HierarchyRequestError');
442
+ }
443
+
444
+ // Check for CSSLayerStatementRule (@layer statement rules)
445
+ if (cssRule.constructor.name === 'CSSLayerStatementRule') {
446
+ errorUtils.throwParseError(this, 'insertRule', this.constructor.name, ruleToParse, 'SyntaxError');
447
+ }
448
+
449
+ cssRule.__parentRule = this;
277
450
  this.cssRules.splice(index, 0, cssRule);
278
451
  return index;
279
452
  };
@@ -291,13 +464,23 @@ CSSOM.CSSGroupingRule.prototype.constructor = CSSOM.CSSGroupingRule;
291
464
  * @see https://www.w3.org/TR/cssom-1/#dom-cssgroupingrule-deleterule
292
465
  */
293
466
  CSSOM.CSSGroupingRule.prototype.deleteRule = function deleteRule(index) {
294
- if (index < 0 || index >= this.cssRules.length) {
295
- throw new RangeError("INDEX_SIZE_ERR");
467
+ if (index === undefined) {
468
+ errorUtils.throwMissingArguments(this, 'deleteRule', this.constructor.name);
296
469
  }
297
- this.cssRules.splice(index, 1)[0].parentRule = null;
470
+ index = Number(index);
471
+ if (index < 0) {
472
+ index = 4294967296 + index;
473
+ }
474
+ if (index >= this.cssRules.length) {
475
+ errorUtils.throwIndexError(this, 'deleteRule', this.constructor.name, index, this.cssRules.length);
476
+ }
477
+ this.cssRules.splice(index, 1)[0].__parentRule = null;
298
478
  };
299
479
 
300
480
 
481
+
482
+
483
+
301
484
  /**
302
485
  * @constructor
303
486
  * @see https://drafts.csswg.org/css-counter-styles/#the-csscounterstylerule-interface
@@ -312,6 +495,9 @@ CSSOM.CSSCounterStyleRule.prototype.constructor = CSSOM.CSSCounterStyleRule;
312
495
  CSSOM.CSSCounterStyleRule.prototype.type = 11;
313
496
 
314
497
 
498
+
499
+
500
+
315
501
  /**
316
502
  * @constructor
317
503
  * @see https://www.w3.org/TR/css-conditional-3/#the-cssconditionrule-interface
@@ -327,6 +513,9 @@ CSSOM.CSSConditionRule.prototype.conditionText = ''
327
513
  CSSOM.CSSConditionRule.prototype.cssText = ''
328
514
 
329
515
 
516
+
517
+
518
+
330
519
  /**
331
520
  * @constructor
332
521
  * @see http://dev.w3.org/csswg/cssom/#cssstylerule
@@ -334,14 +523,40 @@ CSSOM.CSSConditionRule.prototype.cssText = ''
334
523
  */
335
524
  CSSOM.CSSStyleRule = function CSSStyleRule() {
336
525
  CSSOM.CSSGroupingRule.call(this);
337
- this.selectorText = "";
338
- this.style = new CSSOM.CSSStyleDeclaration();
339
- this.style.parentRule = this;
526
+ this.__selectorText = "";
527
+ this.__style = new CSSOM.CSSStyleDeclaration();
528
+ this.__style.parentRule = this;
340
529
  };
341
530
 
342
531
  CSSOM.CSSStyleRule.prototype = new CSSOM.CSSGroupingRule();
343
532
  CSSOM.CSSStyleRule.prototype.constructor = CSSOM.CSSStyleRule;
344
- CSSOM.CSSStyleRule.prototype.type = 1;
533
+
534
+ Object.defineProperty(CSSOM.CSSStyleRule.prototype, "type", {
535
+ value: 1,
536
+ writable: false
537
+ });
538
+
539
+ Object.defineProperty(CSSOM.CSSStyleRule.prototype, "selectorText", {
540
+ get: function() {
541
+ return this.__selectorText;
542
+ },
543
+ set: function(value) {
544
+ this.__selectorText = value;
545
+ }
546
+ });
547
+
548
+ Object.defineProperty(CSSOM.CSSStyleRule.prototype, "style", {
549
+ get: function() {
550
+ return this.__style;
551
+ },
552
+ set: function(value) {
553
+ if (typeof value === "string") {
554
+ this.__style.cssText = value;
555
+ } else {
556
+ this.__style = value;
557
+ }
558
+ }
559
+ });
345
560
 
346
561
  Object.defineProperty(CSSOM.CSSStyleRule.prototype, "cssText", {
347
562
  get: function() {
@@ -354,7 +569,7 @@ Object.defineProperty(CSSOM.CSSStyleRule.prototype, "cssText", {
354
569
  valuesArr.push(this.cssRules.map(function(rule){ return rule.cssText }).join("\n "));
355
570
  values = valuesArr.join("\n ") + "\n}"
356
571
  } else {
357
- values = " { " + this.style.cssText + " }";
572
+ values = " {" + (this.style.cssText ? " " + this.style.cssText : "") + " }";
358
573
  }
359
574
  text = this.selectorText + values;
360
575
  } else {
@@ -364,8 +579,8 @@ Object.defineProperty(CSSOM.CSSStyleRule.prototype, "cssText", {
364
579
  },
365
580
  set: function(cssText) {
366
581
  var rule = CSSOM.CSSStyleRule.parse(cssText);
367
- this.style = rule.style;
368
- this.selectorText = rule.selectorText;
582
+ this.__style = rule.style;
583
+ this.__selectorText = rule.selectorText;
369
584
  }
370
585
  });
371
586
 
@@ -516,6 +731,9 @@ CSSOM.CSSStyleRule.parse = function(ruleText) {
516
731
 
517
732
 
518
733
 
734
+
735
+
736
+
519
737
  /**
520
738
  * @constructor
521
739
  * @see http://dev.w3.org/csswg/cssom/#the-medialist-interface
@@ -570,6 +788,9 @@ CSSOM.MediaList.prototype = {
570
788
 
571
789
 
572
790
 
791
+
792
+
793
+
573
794
  /**
574
795
  * @constructor
575
796
  * @see http://dev.w3.org/csswg/cssom/#cssmediarule
@@ -611,6 +832,9 @@ Object.defineProperties(CSSOM.CSSMediaRule.prototype, {
611
832
 
612
833
 
613
834
 
835
+
836
+
837
+
614
838
  /**
615
839
  * @constructor
616
840
  * @see https://drafts.csswg.org/css-contain-3/
@@ -650,6 +874,9 @@ Object.defineProperties(CSSOM.CSSContainerRule.prototype, {
650
874
 
651
875
 
652
876
 
877
+
878
+
879
+
653
880
  /**
654
881
  * @constructor
655
882
  * @see https://drafts.csswg.org/css-conditional-3/#the-csssupportsrule-interface
@@ -675,6 +902,9 @@ Object.defineProperty(CSSOM.CSSSupportsRule.prototype, "cssText", {
675
902
  });
676
903
 
677
904
 
905
+
906
+
907
+
678
908
  /**
679
909
  * @constructor
680
910
  * @see http://dev.w3.org/csswg/cssom/#cssimportrule
@@ -696,7 +926,7 @@ CSSOM.CSSImportRule.prototype.type = 3;
696
926
  Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", {
697
927
  get: function() {
698
928
  var mediaText = this.media.mediaText;
699
- return "@import url(" + this.href + ")" + (this.layerName !== null ? " layer" + (this.layerName && "(" + this.layerName + ")") : "" ) + (this.supportsText ? " supports(" + this.supportsText + ")" : "" ) + (mediaText ? " " + mediaText : "") + ";";
929
+ return "@import url(\"" + this.href + "\")" + (this.layerName !== null ? " layer" + (this.layerName && "(" + this.layerName + ")") : "" ) + (this.supportsText ? " supports(" + this.supportsText + ")" : "" ) + (mediaText ? " " + mediaText : "") + ";";
700
930
  },
701
931
  set: function(cssText) {
702
932
  var i = 0;
@@ -714,7 +944,7 @@ Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", {
714
944
  var index;
715
945
 
716
946
  var layerRegExp = /layer\(([^)]*)\)/;
717
- var layerRuleNameRegExp = /^(-?[_a-zA-Z]+[_a-zA-Z0-9-]*)$/;
947
+ var layerRuleNameRegExp = /^(-?[_a-zA-Z]+(\.[_a-zA-Z]+)*[_a-zA-Z0-9-]*)$/;
718
948
  var supportsRegExp = /supports\(([^)]+)\)/;
719
949
  var doubleOrMoreSpacesRegExp = /\s{2,}/g;
720
950
 
@@ -797,12 +1027,12 @@ Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", {
797
1027
 
798
1028
  if (layerMatch) {
799
1029
  var layerName = layerMatch[1].trim();
800
- bufferTrimmed = bufferTrimmed.replace(layerRegExp, '')
801
- .replace(doubleOrMoreSpacesRegExp, ' ') // Replace double or more spaces with single space
802
- .trim();
803
1030
 
804
1031
  if (layerName.match(layerRuleNameRegExp) !== null) {
805
1032
  this.layerName = layerMatch[1].trim();
1033
+ bufferTrimmed = bufferTrimmed.replace(layerRegExp, '')
1034
+ .replace(doubleOrMoreSpacesRegExp, ' ') // Replace double or more spaces with single space
1035
+ .trim();
806
1036
  } else {
807
1037
  // REVIEW: In the browser, an empty layer() is not processed as a unamed layer
808
1038
  // and treats the rest of the string as mediaText, ignoring the parse of supports()
@@ -847,6 +1077,9 @@ Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", {
847
1077
 
848
1078
 
849
1079
 
1080
+
1081
+
1082
+
850
1083
  /**
851
1084
  * @constructor
852
1085
  * @see https://drafts.csswg.org/cssom/#the-cssnamespacerule-interface
@@ -914,14 +1147,17 @@ Object.defineProperty(CSSOM.CSSNamespaceRule.prototype, "cssText", {
914
1147
 
915
1148
 
916
1149
 
1150
+
1151
+
1152
+
917
1153
  /**
918
1154
  * @constructor
919
1155
  * @see http://dev.w3.org/csswg/cssom/#css-font-face-rule
920
1156
  */
921
1157
  CSSOM.CSSFontFaceRule = function CSSFontFaceRule() {
922
1158
  CSSOM.CSSRule.call(this);
923
- this.style = new CSSOM.CSSStyleDeclaration();
924
- this.style.parentRule = this;
1159
+ this.__style = new CSSOM.CSSStyleDeclaration();
1160
+ this.__style.parentRule = this;
925
1161
  };
926
1162
 
927
1163
  CSSOM.CSSFontFaceRule.prototype = new CSSOM.CSSRule();
@@ -931,15 +1167,31 @@ CSSOM.CSSFontFaceRule.prototype.type = 5;
931
1167
  //CSSOM.CSSFontFaceRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
932
1168
  //CSSOM.CSSFontFaceRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
933
1169
 
1170
+ Object.defineProperty(CSSOM.CSSFontFaceRule.prototype, "style", {
1171
+ get: function() {
1172
+ return this.__style;
1173
+ },
1174
+ set: function(value) {
1175
+ if (typeof value === "string") {
1176
+ this.__style.cssText = value;
1177
+ } else {
1178
+ this.__style = value;
1179
+ }
1180
+ }
1181
+ });
1182
+
934
1183
  // http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSFontFaceRule.cpp
935
1184
  Object.defineProperty(CSSOM.CSSFontFaceRule.prototype, "cssText", {
936
1185
  get: function() {
937
- return "@font-face { " + this.style.cssText + " }";
1186
+ return "@font-face {" + (this.style.cssText ? " " + this.style.cssText : "") + " }";
938
1187
  }
939
1188
  });
940
1189
 
941
1190
 
942
1191
 
1192
+
1193
+
1194
+
943
1195
  /**
944
1196
  * @constructor
945
1197
  * @see http://www.w3.org/TR/shadow-dom/#host-at-rule
@@ -968,6 +1220,9 @@ Object.defineProperty(CSSOM.CSSHostRule.prototype, "cssText", {
968
1220
 
969
1221
 
970
1222
 
1223
+
1224
+
1225
+
971
1226
  /**
972
1227
  * @constructor
973
1228
  * @see http://www.w3.org/TR/shadow-dom/#host-at-rule
@@ -996,14 +1251,27 @@ Object.defineProperty(CSSOM.CSSStartingStyleRule.prototype, "cssText", {
996
1251
 
997
1252
 
998
1253
 
1254
+
1255
+
1256
+
999
1257
  /**
1000
1258
  * @constructor
1001
1259
  * @see http://dev.w3.org/csswg/cssom/#the-stylesheet-interface
1002
1260
  */
1003
1261
  CSSOM.StyleSheet = function StyleSheet() {
1004
- this.parentStyleSheet = null;
1262
+ this.__parentStyleSheet = null;
1005
1263
  };
1006
1264
 
1265
+ Object.defineProperties(CSSOM.StyleSheet.prototype, {
1266
+ parentStyleSheet: {
1267
+ get: function() {
1268
+ return this.__parentStyleSheet;
1269
+ }
1270
+ }
1271
+ });
1272
+
1273
+
1274
+
1007
1275
 
1008
1276
 
1009
1277
  /**
@@ -1038,25 +1306,113 @@ CSSOM.CSSStyleSheet.prototype.constructor = CSSOM.CSSStyleSheet;
1038
1306
  */
1039
1307
  CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) {
1040
1308
  if (rule === undefined && index === undefined) {
1041
- throw new (this._globalObject && this._globalObject.TypeError || TypeError)("Failed to execute 'insertRule' on 'CSSStyleSheet': 1 argument required, but only 0 present.")
1309
+ errorUtils.throwMissingArguments(this, 'insertRule', this.constructor.name);
1042
1310
  }
1043
1311
  if (index === void 0) {
1044
1312
  index = 0;
1045
1313
  }
1046
- if (index < 0 || index > this.cssRules.length) {
1047
- throw new (this._globalObject && this._globalObject.RangeError || RangeError)("INDEX_SIZE_ERR");
1314
+ index = Number(index);
1315
+ if (index < 0) {
1316
+ index = 4294967296 + index;
1048
1317
  }
1318
+ if (index > this.cssRules.length) {
1319
+ errorUtils.throwIndexError(this, 'insertRule', this.constructor.name, index, this.cssRules.length);
1320
+ }
1321
+
1049
1322
  var ruleToParse = String(rule);
1050
1323
  var parsedSheet = CSSOM.parse(ruleToParse);
1051
1324
  if (parsedSheet.cssRules.length !== 1) {
1052
- var domExceptionName = "SyntaxError";
1053
- if (ruleToParse.trimStart().startsWith('@namespace')) {
1054
- domExceptionName = "InvalidStateError";
1055
- }
1056
- throw new (this._globalObject && this._globalObject.DOMException || DOMException)("Failed to execute 'insertRule' on 'CSSStyleSheet': Failed to parse the rule '" + ruleToParse + "'.", domExceptionName);
1325
+ errorUtils.throwParseError(this, 'insertRule', this.constructor.name, ruleToParse, 'SyntaxError');
1057
1326
  }
1058
1327
  var cssRule = parsedSheet.cssRules[0];
1059
- cssRule.parentStyleSheet = this;
1328
+
1329
+ // Helper function to find the last index of a specific rule constructor
1330
+ function findLastIndexOfConstructor(rules, constructorName) {
1331
+ for (var i = rules.length - 1; i >= 0; i--) {
1332
+ if (rules[i].constructor.name === constructorName) {
1333
+ return i;
1334
+ }
1335
+ }
1336
+ return -1;
1337
+ }
1338
+
1339
+ // Helper function to find the first index of a rule that's NOT of specified constructors
1340
+ function findFirstNonConstructorIndex(rules, constructorNames) {
1341
+ for (var i = 0; i < rules.length; i++) {
1342
+ if (constructorNames.indexOf(rules[i].constructor.name) === -1) {
1343
+ return i;
1344
+ }
1345
+ }
1346
+ return rules.length;
1347
+ }
1348
+
1349
+ // Validate rule ordering based on CSS specification
1350
+ if (cssRule.constructor.name === 'CSSImportRule') {
1351
+ // @import rules cannot be inserted after @layer rules that already exist
1352
+ // They can only be inserted at the beginning or after other @import rules
1353
+ var firstLayerIndex = findFirstNonConstructorIndex(this.cssRules, ['CSSImportRule']);
1354
+ if (firstLayerIndex < this.cssRules.length && this.cssRules[firstLayerIndex].constructor.name === 'CSSLayerStatementRule' && index > firstLayerIndex) {
1355
+ errorUtils.throwError(this, 'DOMException',
1356
+ "Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.",
1357
+ 'HierarchyRequestError');
1358
+ }
1359
+
1360
+ // Also cannot insert after @namespace or other rules
1361
+ var firstNonImportIndex = findFirstNonConstructorIndex(this.cssRules, ['CSSImportRule']);
1362
+ if (index > firstNonImportIndex && firstNonImportIndex < this.cssRules.length &&
1363
+ this.cssRules[firstNonImportIndex].constructor.name !== 'CSSLayerStatementRule') {
1364
+ errorUtils.throwError(this, 'DOMException',
1365
+ "Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.",
1366
+ 'HierarchyRequestError');
1367
+ }
1368
+ } else if (cssRule.constructor.name === 'CSSNamespaceRule') {
1369
+ // @namespace rules can come after @layer and @import, but before any other rules
1370
+ // They cannot come before @import rules
1371
+ var firstImportIndex = -1;
1372
+ for (var i = 0; i < this.cssRules.length; i++) {
1373
+ if (this.cssRules[i].constructor.name === 'CSSImportRule') {
1374
+ firstImportIndex = i;
1375
+ break;
1376
+ }
1377
+ }
1378
+ var firstNonImportNamespaceIndex = findFirstNonConstructorIndex(this.cssRules, [
1379
+ 'CSSLayerStatementRule',
1380
+ 'CSSImportRule',
1381
+ 'CSSNamespaceRule'
1382
+ ]);
1383
+
1384
+ // Cannot insert before @import rules
1385
+ if (firstImportIndex !== -1 && index <= firstImportIndex) {
1386
+ errorUtils.throwError(this, 'DOMException',
1387
+ "Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.",
1388
+ 'HierarchyRequestError');
1389
+ }
1390
+
1391
+ // Cannot insert after other types of rules
1392
+ if (index > firstNonImportNamespaceIndex) {
1393
+ errorUtils.throwError(this, 'DOMException',
1394
+ "Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.",
1395
+ 'HierarchyRequestError');
1396
+ }
1397
+ } else if (cssRule.constructor.name === 'CSSLayerStatementRule') {
1398
+ // @layer statement rules can be inserted anywhere before @import and @namespace
1399
+ // No additional restrictions beyond what's already handled
1400
+ } else {
1401
+ // Any other rule cannot be inserted before @import and @namespace
1402
+ var firstNonSpecialRuleIndex = findFirstNonConstructorIndex(this.cssRules, [
1403
+ 'CSSLayerStatementRule',
1404
+ 'CSSImportRule',
1405
+ 'CSSNamespaceRule'
1406
+ ]);
1407
+
1408
+ if (index < firstNonSpecialRuleIndex) {
1409
+ errorUtils.throwError(this, 'DOMException',
1410
+ "Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.",
1411
+ 'HierarchyRequestError');
1412
+ }
1413
+ }
1414
+
1415
+ cssRule.__parentStyleSheet = this;
1060
1416
  this.cssRules.splice(index, 0, cssRule);
1061
1417
  return index;
1062
1418
  };
@@ -1077,21 +1433,21 @@ CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) {
1077
1433
  */
1078
1434
  CSSOM.CSSStyleSheet.prototype.deleteRule = function(index) {
1079
1435
  if (index === undefined) {
1080
- throw new (this._globalObject && this._globalObject.TypeError || TypeError)("Failed to execute 'deleteRule' on 'CSSStyleSheet': 1 argument required, but only 0 present.")
1436
+ errorUtils.throwMissingArguments(this, 'deleteRule', this.constructor.name);
1081
1437
  }
1082
1438
  index = Number(index);
1083
1439
  if (index < 0) {
1084
1440
  index = 4294967296 + index;
1085
1441
  }
1086
1442
  if (index >= this.cssRules.length) {
1087
- throw new (this._globalObject && this._globalObject.DOMException || DOMException)("Failed to execute 'deleteRule' on 'CSSStyleSheet': The index provided (" + index + ") is larger than the maximum index (" + this.cssRules.length + ").", "IndexSizeError");
1443
+ errorUtils.throwIndexError(this, 'deleteRule', this.constructor.name, index, this.cssRules.length);
1088
1444
  }
1089
1445
  if (this.cssRules[index] && this.cssRules[index].constructor.name == "CSSNamespaceRule") {
1090
1446
  var shouldContinue = this.cssRules.every(function (rule) {
1091
1447
  return ['CSSImportRule','CSSLayerStatementRule','CSSNamespaceRule'].indexOf(rule.constructor.name) !== -1
1092
1448
  });
1093
1449
  if (!shouldContinue) {
1094
- throw new (this._globalObject && this._globalObject.DOMException || DOMException)("Failed to execute 'deleteRule' on 'CSSStyleSheet': Deleting a CSSNamespaceRule is not allowed when there is rules other than @import, @layer statement, or @namespace.", "InvalidStateError");
1450
+ errorUtils.throwError(this, 'DOMException', "Failed to execute 'deleteRule' on '" + this.constructor.name + "': Failed to delete rule.", "InvalidStateError");
1095
1451
  }
1096
1452
  }
1097
1453
  this.cssRules.splice(index, 1);
@@ -1113,6 +1469,9 @@ CSSOM.CSSStyleSheet.prototype.toString = function() {
1113
1469
 
1114
1470
 
1115
1471
 
1472
+
1473
+
1474
+
1116
1475
  /**
1117
1476
  * @constructor
1118
1477
  * @see http://www.w3.org/TR/css3-animations/#DOM-CSSKeyframesRule
@@ -1121,14 +1480,42 @@ CSSOM.CSSKeyframesRule = function CSSKeyframesRule() {
1121
1480
  CSSOM.CSSRule.call(this);
1122
1481
  this.name = '';
1123
1482
  this.cssRules = [];
1483
+
1484
+ // Set up initial indexed access
1485
+ this._setupIndexedAccess();
1486
+
1487
+ // Override cssRules methods after initial setup, store references as non-enumerable properties
1488
+ var self = this;
1489
+ var originalPush = this.cssRules.push;
1490
+ var originalSplice = this.cssRules.splice;
1491
+
1492
+ // Create non-enumerable method overrides
1493
+ Object.defineProperty(this.cssRules, 'push', {
1494
+ value: function() {
1495
+ var result = originalPush.apply(this, arguments);
1496
+ self._setupIndexedAccess();
1497
+ return result;
1498
+ },
1499
+ writable: true,
1500
+ enumerable: false,
1501
+ configurable: true
1502
+ });
1503
+
1504
+ Object.defineProperty(this.cssRules, 'splice', {
1505
+ value: function() {
1506
+ var result = originalSplice.apply(this, arguments);
1507
+ self._setupIndexedAccess();
1508
+ return result;
1509
+ },
1510
+ writable: true,
1511
+ enumerable: false,
1512
+ configurable: true
1513
+ });
1124
1514
  };
1125
1515
 
1126
1516
  CSSOM.CSSKeyframesRule.prototype = new CSSOM.CSSRule();
1127
1517
  CSSOM.CSSKeyframesRule.prototype.constructor = CSSOM.CSSKeyframesRule;
1128
1518
  CSSOM.CSSKeyframesRule.prototype.type = 7;
1129
- //FIXME
1130
- //CSSOM.CSSKeyframesRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
1131
- //CSSOM.CSSKeyframesRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
1132
1519
 
1133
1520
  // http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSKeyframesRule.cpp
1134
1521
  Object.defineProperty(CSSOM.CSSKeyframesRule.prototype, "cssText", {
@@ -1137,10 +1524,177 @@ Object.defineProperty(CSSOM.CSSKeyframesRule.prototype, "cssText", {
1137
1524
  for (var i=0, length=this.cssRules.length; i < length; i++) {
1138
1525
  cssTexts.push(this.cssRules[i].cssText);
1139
1526
  }
1140
- return "@" + (this._vendorPrefix || '') + "keyframes " + this.name + (this.name && " ") + "{" + (cssTexts.length ? "\n " + cssTexts.join("\n ") : "") + "\n}";
1527
+ var cssWideKeywords = ['initial', 'inherit', 'revert', 'revert-layer', 'unset', 'none'];
1528
+ var processedName = cssWideKeywords.includes(this.name) ? '"' + this.name + '"' : this.name;
1529
+ return "@" + (this._vendorPrefix || '') + "keyframes " + processedName + (processedName && " ") + "{" + (cssTexts.length ? "\n " + cssTexts.join("\n ") : "") + "\n}";
1141
1530
  }
1142
1531
  });
1143
1532
 
1533
+ /**
1534
+ * Appends a new keyframe rule to the list of keyframes.
1535
+ *
1536
+ * @param {string} rule - The keyframe rule string to append (e.g., "50% { opacity: 0.5; }")
1537
+ * @see https://www.w3.org/TR/css-animations-1/#dom-csskeyframesrule-appendrule
1538
+ */
1539
+ CSSOM.CSSKeyframesRule.prototype.appendRule = function appendRule(rule) {
1540
+ if (arguments.length === 0) {
1541
+ errorUtils.throwMissingArguments(this, 'appendRule', 'CSSKeyframesRule');
1542
+ }
1543
+
1544
+ var parsedRule;
1545
+ try {
1546
+ // Parse the rule string as a keyframe rule
1547
+ var tempStyleSheet = CSSOM.parse("@keyframes temp { " + rule + " }");
1548
+ if (tempStyleSheet.cssRules.length > 0 && tempStyleSheet.cssRules[0].cssRules.length > 0) {
1549
+ parsedRule = tempStyleSheet.cssRules[0].cssRules[0];
1550
+ } else {
1551
+ throw new Error("Failed to parse keyframe rule");
1552
+ }
1553
+ } catch (e) {
1554
+ errorUtils.throwParseError(this, 'appendRule', 'CSSKeyframesRule', rule);
1555
+ }
1556
+
1557
+ parsedRule.__parentRule = this;
1558
+ this.cssRules.push(parsedRule);
1559
+ };
1560
+
1561
+ /**
1562
+ * Deletes a keyframe rule that matches the specified key.
1563
+ *
1564
+ * @param {string} select - The keyframe selector to delete (e.g., "50%", "from", "to")
1565
+ * @see https://www.w3.org/TR/css-animations-1/#dom-csskeyframesrule-deleterule
1566
+ */
1567
+ CSSOM.CSSKeyframesRule.prototype.deleteRule = function deleteRule(select) {
1568
+ if (arguments.length === 0) {
1569
+ errorUtils.throwMissingArguments(this, 'deleteRule', 'CSSKeyframesRule');
1570
+ }
1571
+
1572
+ var normalizedSelect = this._normalizeKeyText(select);
1573
+
1574
+ for (var i = 0; i < this.cssRules.length; i++) {
1575
+ var rule = this.cssRules[i];
1576
+ if (this._normalizeKeyText(rule.keyText) === normalizedSelect) {
1577
+ rule.__parentRule = null;
1578
+ this.cssRules.splice(i, 1);
1579
+ return;
1580
+ }
1581
+ }
1582
+ };
1583
+
1584
+ /**
1585
+ * Finds and returns the keyframe rule that matches the specified key.
1586
+ * When multiple rules have the same key, returns the last one.
1587
+ *
1588
+ * @param {string} select - The keyframe selector to find (e.g., "50%", "from", "to")
1589
+ * @return {CSSKeyframeRule|null} The matching keyframe rule, or null if not found
1590
+ * @see https://www.w3.org/TR/css-animations-1/#dom-csskeyframesrule-findrule
1591
+ */
1592
+ CSSOM.CSSKeyframesRule.prototype.findRule = function findRule(select) {
1593
+ if (arguments.length === 0) {
1594
+ errorUtils.throwMissingArguments(this, 'findRule', 'CSSKeyframesRule');
1595
+ }
1596
+
1597
+ var normalizedSelect = this._normalizeKeyText(select);
1598
+
1599
+ // Iterate backwards to find the last matching rule
1600
+ for (var i = this.cssRules.length - 1; i >= 0; i--) {
1601
+ var rule = this.cssRules[i];
1602
+ if (this._normalizeKeyText(rule.keyText) === normalizedSelect) {
1603
+ return rule;
1604
+ }
1605
+ }
1606
+
1607
+ return null;
1608
+ };
1609
+
1610
+ /**
1611
+ * Normalizes keyframe selector text for comparison.
1612
+ * Handles "from" -> "0%" and "to" -> "100%" conversions and trims whitespace.
1613
+ *
1614
+ * @private
1615
+ * @param {string} keyText - The keyframe selector text to normalize
1616
+ * @return {string} The normalized keyframe selector text
1617
+ */
1618
+ CSSOM.CSSKeyframesRule.prototype._normalizeKeyText = function _normalizeKeyText(keyText) {
1619
+ if (!keyText) return '';
1620
+
1621
+ var normalized = keyText.toString().trim().toLowerCase();
1622
+
1623
+ // Convert keywords to percentages for comparison
1624
+ if (normalized === 'from') {
1625
+ return '0%';
1626
+ } else if (normalized === 'to') {
1627
+ return '100%';
1628
+ }
1629
+
1630
+ return normalized;
1631
+ };
1632
+
1633
+ /**
1634
+ * Makes CSSKeyframesRule iterable over its cssRules.
1635
+ * Allows for...of loops and other iterable methods.
1636
+ */
1637
+ if (typeof Symbol !== 'undefined' && Symbol.iterator) {
1638
+ CSSOM.CSSKeyframesRule.prototype[Symbol.iterator] = function() {
1639
+ var index = 0;
1640
+ var cssRules = this.cssRules;
1641
+
1642
+ return {
1643
+ next: function() {
1644
+ if (index < cssRules.length) {
1645
+ return { value: cssRules[index++], done: false };
1646
+ } else {
1647
+ return { done: true };
1648
+ }
1649
+ }
1650
+ };
1651
+ };
1652
+ }
1653
+
1654
+ /**
1655
+ * Adds indexed getters for direct access to cssRules by index.
1656
+ * This enables rule[0], rule[1], etc. access patterns.
1657
+ * Works in environments where Proxy is not available (like jsdom).
1658
+ */
1659
+ CSSOM.CSSKeyframesRule.prototype._setupIndexedAccess = function() {
1660
+ // Remove any existing indexed properties
1661
+ for (var i = 0; i < 1000; i++) { // reasonable upper limit
1662
+ if (this.hasOwnProperty(i)) {
1663
+ delete this[i];
1664
+ } else {
1665
+ break;
1666
+ }
1667
+ }
1668
+
1669
+ // Add indexed getters for current cssRules
1670
+ for (var i = 0; i < this.cssRules.length; i++) {
1671
+ (function(index) {
1672
+ Object.defineProperty(this, index, {
1673
+ get: function() {
1674
+ return this.cssRules[index];
1675
+ },
1676
+ enumerable: false,
1677
+ configurable: true
1678
+ });
1679
+ }.call(this, i));
1680
+ }
1681
+
1682
+ // Update length property
1683
+ Object.defineProperty(this, 'length', {
1684
+ get: function() {
1685
+ return this.cssRules.length;
1686
+ },
1687
+ enumerable: false,
1688
+ configurable: true
1689
+ });
1690
+ };
1691
+
1692
+
1693
+
1694
+
1695
+
1696
+
1697
+
1144
1698
 
1145
1699
 
1146
1700
  /**
@@ -1150,8 +1704,8 @@ Object.defineProperty(CSSOM.CSSKeyframesRule.prototype, "cssText", {
1150
1704
  CSSOM.CSSKeyframeRule = function CSSKeyframeRule() {
1151
1705
  CSSOM.CSSRule.call(this);
1152
1706
  this.keyText = '';
1153
- this.style = new CSSOM.CSSStyleDeclaration();
1154
- this.style.parentRule = this;
1707
+ this.__style = new CSSOM.CSSStyleDeclaration();
1708
+ this.__style.parentRule = this;
1155
1709
  };
1156
1710
 
1157
1711
  CSSOM.CSSKeyframeRule.prototype = new CSSOM.CSSRule();
@@ -1161,15 +1715,31 @@ CSSOM.CSSKeyframeRule.prototype.type = 8;
1161
1715
  //CSSOM.CSSKeyframeRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
1162
1716
  //CSSOM.CSSKeyframeRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
1163
1717
 
1718
+ Object.defineProperty(CSSOM.CSSKeyframeRule.prototype, "style", {
1719
+ get: function() {
1720
+ return this.__style;
1721
+ },
1722
+ set: function(value) {
1723
+ if (typeof value === "string") {
1724
+ this.__style.cssText = value;
1725
+ } else {
1726
+ this.__style = value;
1727
+ }
1728
+ }
1729
+ });
1730
+
1164
1731
  // http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSKeyframeRule.cpp
1165
1732
  Object.defineProperty(CSSOM.CSSKeyframeRule.prototype, "cssText", {
1166
1733
  get: function() {
1167
- return this.keyText + " { " + this.style.cssText + " } ";
1734
+ return this.keyText + " {" + (this.style.cssText ? " " + this.style.cssText : "") + " }";
1168
1735
  }
1169
1736
  });
1170
1737
 
1171
1738
 
1172
1739
 
1740
+
1741
+
1742
+
1173
1743
  /**
1174
1744
  * @constructor
1175
1745
  * @see https://developer.mozilla.org/en/CSS/@-moz-document
@@ -1225,6 +1795,9 @@ CSSOM.MatcherList.prototype = {
1225
1795
 
1226
1796
 
1227
1797
 
1798
+
1799
+
1800
+
1228
1801
  /**
1229
1802
  * @constructor
1230
1803
  * @see https://developer.mozilla.org/en/CSS/@-moz-document
@@ -1254,6 +1827,9 @@ Object.defineProperty(CSSOM.CSSDocumentRule.prototype, "cssText", {
1254
1827
 
1255
1828
 
1256
1829
 
1830
+
1831
+
1832
+
1257
1833
  /**
1258
1834
  * @constructor
1259
1835
  * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue
@@ -1290,6 +1866,9 @@ CSSOM.CSSValue.prototype = {
1290
1866
 
1291
1867
 
1292
1868
 
1869
+
1870
+
1871
+
1293
1872
  /**
1294
1873
  * @constructor
1295
1874
  * @see http://msdn.microsoft.com/en-us/library/ms537634(v=vs.85).aspx
@@ -1625,6 +2204,8 @@ CSSOM.CSSValueExpression.prototype._findMatchedIdx = function(token, idx, sep) {
1625
2204
 
1626
2205
 
1627
2206
 
2207
+
2208
+
1628
2209
  /**
1629
2210
  * @constructor
1630
2211
  * @see https://drafts.csswg.org/css-cascade-5/#csslayerblockrule
@@ -1654,6 +2235,8 @@ Object.defineProperties(CSSOM.CSSLayerBlockRule.prototype, {
1654
2235
  });
1655
2236
 
1656
2237
 
2238
+
2239
+
1657
2240
  /**
1658
2241
  * @constructor
1659
2242
  * @see https://drafts.csswg.org/css-cascade-5/#csslayerstatementrule
@@ -1678,11 +2261,20 @@ Object.defineProperties(CSSOM.CSSLayerStatementRule.prototype, {
1678
2261
  });
1679
2262
 
1680
2263
 
2264
+
2265
+
2266
+
1681
2267
  /**
1682
- * @param {string} token
2268
+ * Parses a CSS string and returns a CSSOM.CSSStyleSheet object representing the parsed stylesheet.
2269
+ *
2270
+ * @param {string} token - The CSS string to parse.
2271
+ * @param {object} [opts] - Optional parsing options.
2272
+ * @param {object} [opts.globalObject] - An optional global object to attach to the stylesheet. Useful on jsdom webplatform tests.
2273
+ * @param {function|boolean} [errorHandler] - Optional error handler function or `true` to use `console.error`.
2274
+ * @returns {CSSOM.CSSStyleSheet} The parsed CSSStyleSheet object.
1683
2275
  */
1684
- CSSOM.parse = function parse(token, errorHandler) {
1685
- errorHandler = errorHandler === undefined ? (console && console.error) : errorHandler;
2276
+ CSSOM.parse = function parse(token, opts, errorHandler) {
2277
+ errorHandler = errorHandler === true ? (console && console.error) : errorHandler;
1686
2278
 
1687
2279
  var i = 0;
1688
2280
 
@@ -1724,6 +2316,10 @@ CSSOM.parse = function parse(token, errorHandler) {
1724
2316
 
1725
2317
  var styleSheet = new CSSOM.CSSStyleSheet();
1726
2318
 
2319
+ if (opts && opts.globalObject) {
2320
+ styleSheet.__globalObject = opts.globalObject;
2321
+ }
2322
+
1727
2323
  // @type CSSStyleSheet|CSSMediaRule|CSSContainerRule|CSSSupportsRule|CSSFontFaceRule|CSSKeyframesRule|CSSDocumentRule
1728
2324
  var currentScope = styleSheet;
1729
2325
 
@@ -2421,7 +3017,7 @@ CSSOM.parse = function parse(token, errorHandler) {
2421
3017
  i += "font-face".length;
2422
3018
  fontFaceRule = new CSSOM.CSSFontFaceRule();
2423
3019
  fontFaceRule.__starts = i;
2424
- }, parentRule && parentRule.constructor.name === "CSSStyleRule" );
3020
+ }, true);
2425
3021
  break;
2426
3022
  } else {
2427
3023
  atKeyframesRegExp.lastIndex = i;
@@ -2463,7 +3059,7 @@ CSSOM.parse = function parse(token, errorHandler) {
2463
3059
  }
2464
3060
 
2465
3061
  if (parentRule) {
2466
- styleRule.parentRule = parentRule;
3062
+ styleRule.__parentRule = parentRule;
2467
3063
  ancestorRules.push(parentRule);
2468
3064
  }
2469
3065
 
@@ -2473,48 +3069,48 @@ CSSOM.parse = function parse(token, errorHandler) {
2473
3069
  return match;
2474
3070
  });
2475
3071
  styleRule.style.__starts = i;
2476
- styleRule.parentStyleSheet = styleSheet;
3072
+ styleRule.__parentStyleSheet = styleSheet;
2477
3073
  buffer = "";
2478
3074
  state = "before-name";
2479
3075
  } else if (state === "atBlock") {
2480
3076
  mediaRule.media.mediaText = buffer.trim();
2481
3077
 
2482
3078
  if (parentRule) {
2483
- mediaRule.parentRule = parentRule;
3079
+ mediaRule.__parentRule = parentRule;
2484
3080
  ancestorRules.push(parentRule);
2485
3081
  }
2486
3082
 
2487
3083
  currentScope = parentRule = mediaRule;
2488
- mediaRule.parentStyleSheet = styleSheet;
3084
+ mediaRule.__parentStyleSheet = styleSheet;
2489
3085
  buffer = "";
2490
3086
  state = "before-selector";
2491
3087
  } else if (state === "containerBlock") {
2492
3088
  containerRule.containerText = buffer.trim();
2493
3089
 
2494
3090
  if (parentRule) {
2495
- containerRule.parentRule = parentRule;
3091
+ containerRule.__parentRule = parentRule;
2496
3092
  ancestorRules.push(parentRule);
2497
3093
  }
2498
3094
  currentScope = parentRule = containerRule;
2499
- containerRule.parentStyleSheet = styleSheet;
3095
+ containerRule.__parentStyleSheet = styleSheet;
2500
3096
  buffer = "";
2501
3097
  state = "before-selector";
2502
3098
  } else if (state === "counterStyleBlock") {
2503
3099
  // TODO: Validate counter-style name. At least that it cannot be empty nor multiple
2504
3100
  counterStyleRule.name = buffer.trim().replace(/\n/g, "");
2505
3101
  currentScope = parentRule = counterStyleRule;
2506
- counterStyleRule.parentStyleSheet = styleSheet;
3102
+ counterStyleRule.__parentStyleSheet = styleSheet;
2507
3103
  buffer = "";
2508
3104
  } else if (state === "conditionBlock") {
2509
3105
  supportsRule.conditionText = buffer.trim();
2510
3106
 
2511
3107
  if (parentRule) {
2512
- supportsRule.parentRule = parentRule;
3108
+ supportsRule.__parentRule = parentRule;
2513
3109
  ancestorRules.push(parentRule);
2514
3110
  }
2515
3111
 
2516
3112
  currentScope = parentRule = supportsRule;
2517
- supportsRule.parentStyleSheet = styleSheet;
3113
+ supportsRule.__parentStyleSheet = styleSheet;
2518
3114
  buffer = "";
2519
3115
  state = "before-selector";
2520
3116
  } else if (state === "layerBlock") {
@@ -2524,12 +3120,12 @@ CSSOM.parse = function parse(token, errorHandler) {
2524
3120
 
2525
3121
  if (isValidName) {
2526
3122
  if (parentRule) {
2527
- layerBlockRule.parentRule = parentRule;
3123
+ layerBlockRule.__parentRule = parentRule;
2528
3124
  ancestorRules.push(parentRule);
2529
3125
  }
2530
3126
 
2531
3127
  currentScope = parentRule = layerBlockRule;
2532
- layerBlockRule.parentStyleSheet = styleSheet;
3128
+ layerBlockRule.__parentStyleSheet = styleSheet;
2533
3129
  }
2534
3130
  buffer = "";
2535
3131
  state = "before-selector";
@@ -2539,25 +3135,25 @@ CSSOM.parse = function parse(token, errorHandler) {
2539
3135
  }
2540
3136
 
2541
3137
  currentScope = parentRule = hostRule;
2542
- hostRule.parentStyleSheet = styleSheet;
3138
+ hostRule.__parentStyleSheet = styleSheet;
2543
3139
  buffer = "";
2544
3140
  state = "before-selector";
2545
3141
  } else if (state === "startingStyleRule-begin") {
2546
3142
  if (parentRule) {
2547
- startingStyleRule.parentRule = parentRule;
3143
+ startingStyleRule.__parentRule = parentRule;
2548
3144
  ancestorRules.push(parentRule);
2549
3145
  }
2550
3146
 
2551
3147
  currentScope = parentRule = startingStyleRule;
2552
- startingStyleRule.parentStyleSheet = styleSheet;
3148
+ startingStyleRule.__parentStyleSheet = styleSheet;
2553
3149
  buffer = "";
2554
3150
  state = "before-selector";
2555
3151
 
2556
3152
  } else if (state === "fontFaceRule-begin") {
2557
3153
  if (parentRule) {
2558
- fontFaceRule.parentRule = parentRule;
3154
+ fontFaceRule.__parentRule = parentRule;
2559
3155
  }
2560
- fontFaceRule.parentStyleSheet = styleSheet;
3156
+ fontFaceRule.__parentStyleSheet = styleSheet;
2561
3157
  styleRule = fontFaceRule;
2562
3158
  buffer = "";
2563
3159
  state = "before-name";
@@ -2565,9 +3161,9 @@ CSSOM.parse = function parse(token, errorHandler) {
2565
3161
  keyframesRule.name = buffer.trim();
2566
3162
  if (parentRule) {
2567
3163
  ancestorRules.push(parentRule);
2568
- keyframesRule.parentRule = parentRule;
3164
+ keyframesRule.__parentRule = parentRule;
2569
3165
  }
2570
- keyframesRule.parentStyleSheet = styleSheet;
3166
+ keyframesRule.__parentStyleSheet = styleSheet;
2571
3167
  currentScope = parentRule = keyframesRule;
2572
3168
  buffer = "";
2573
3169
  state = "keyframeRule-begin";
@@ -2582,18 +3178,18 @@ CSSOM.parse = function parse(token, errorHandler) {
2582
3178
  documentRule.matcher.matcherText = buffer.trim();
2583
3179
  if (parentRule) {
2584
3180
  ancestorRules.push(parentRule);
2585
- documentRule.parentRule = parentRule;
3181
+ documentRule.__parentRule = parentRule;
2586
3182
  }
2587
3183
  currentScope = parentRule = documentRule;
2588
- documentRule.parentStyleSheet = styleSheet;
3184
+ documentRule.__parentStyleSheet = styleSheet;
2589
3185
  buffer = "";
2590
3186
  state = "before-selector";
2591
3187
  } else if (state === "before-name" || state === "name") {
2592
3188
  if (styleRule.constructor.name === "CSSNestedDeclarations") {
2593
3189
  if (styleRule.style.length) {
2594
3190
  parentRule.cssRules.push(styleRule);
2595
- styleRule.parentRule = parentRule;
2596
- styleRule.parentStyleSheet = styleSheet;
3191
+ styleRule.__parentRule = parentRule;
3192
+ styleRule.__parentStyleSheet = styleSheet;
2597
3193
  ancestorRules.push(parentRule);
2598
3194
  } else {
2599
3195
  // If the styleRule is empty, we can assume that it's a nested selector
@@ -2602,7 +3198,7 @@ CSSOM.parse = function parse(token, errorHandler) {
2602
3198
  } else {
2603
3199
  currentScope = parentRule = styleRule;
2604
3200
  ancestorRules.push(parentRule);
2605
- styleRule.parentStyleSheet = styleSheet;
3201
+ styleRule.__parentStyleSheet = styleSheet;
2606
3202
  }
2607
3203
 
2608
3204
  styleRule = new CSSOM.CSSStyleRule();
@@ -2619,7 +3215,7 @@ CSSOM.parse = function parse(token, errorHandler) {
2619
3215
  }).join(', ');
2620
3216
  }
2621
3217
  styleRule.style.__starts = i - buffer.length;
2622
- styleRule.parentRule = parentRule;
3218
+ styleRule.__parentRule = parentRule;
2623
3219
  nestedSelectorRule = styleRule;
2624
3220
 
2625
3221
  buffer = "";
@@ -2714,7 +3310,7 @@ CSSOM.parse = function parse(token, errorHandler) {
2714
3310
  });
2715
3311
  if (isValid) {
2716
3312
  importRule = new CSSOM.CSSImportRule();
2717
- importRule.parentStyleSheet = importRule.styleSheet.parentStyleSheet = styleSheet;
3313
+ importRule.__parentStyleSheet = importRule.styleSheet.__parentStyleSheet = styleSheet;
2718
3314
  importRule.cssText = buffer + character;
2719
3315
  styleSheet.cssRules.push(importRule);
2720
3316
  }
@@ -2732,7 +3328,7 @@ CSSOM.parse = function parse(token, errorHandler) {
2732
3328
  testNamespaceRule.cssText = buffer + character;
2733
3329
 
2734
3330
  namespaceRule = testNamespaceRule;
2735
- namespaceRule.parentStyleSheet = namespaceRule.styleSheet.parentStyleSheet = styleSheet;
3331
+ namespaceRule.__parentStyleSheet = namespaceRule.styleSheet.__parentStyleSheet = styleSheet;
2736
3332
  styleSheet.cssRules.push(namespaceRule);
2737
3333
 
2738
3334
  // Track the namespace prefix for validation
@@ -2756,7 +3352,7 @@ CSSOM.parse = function parse(token, errorHandler) {
2756
3352
 
2757
3353
  if (!isInvalid) {
2758
3354
  layerStatementRule = new CSSOM.CSSLayerStatementRule();
2759
- layerStatementRule.parentStyleSheet = styleSheet;
3355
+ layerStatementRule.__parentStyleSheet = styleSheet;
2760
3356
  layerStatementRule.__starts = layerBlockRule.__starts;
2761
3357
  layerStatementRule.__ends = i;
2762
3358
  layerStatementRule.nameList = nameListStr;
@@ -2796,9 +3392,9 @@ CSSOM.parse = function parse(token, errorHandler) {
2796
3392
  }
2797
3393
 
2798
3394
  if (parentRule) {
2799
- styleRule.parentRule = parentRule;
3395
+ styleRule.__parentRule = parentRule;
2800
3396
  }
2801
- styleRule.parentStyleSheet = styleSheet;
3397
+ styleRule.__parentStyleSheet = styleSheet;
2802
3398
 
2803
3399
  if (currentScope === styleRule) {
2804
3400
  currentScope = parentRule || styleSheet;
@@ -2924,7 +3520,7 @@ CSSOM.parse = function parse(token, errorHandler) {
2924
3520
  || parentRule.constructor.name === "CSSLayerBlockRule"
2925
3521
  || parentRule.constructor.name === "CSSStartingStyleRule"
2926
3522
  ) {
2927
- // parentRule.parentRule = styleRule;
3523
+ // parentRule.__parentRule = styleRule;
2928
3524
  state = "before-name";
2929
3525
  if (styleRule !== parentRule) {
2930
3526
  styleRule = new CSSOM.CSSNestedDeclarations();
@@ -2981,6 +3577,9 @@ CSSOM.parse = function parse(token, errorHandler) {
2981
3577
 
2982
3578
 
2983
3579
 
3580
+
3581
+
3582
+
2984
3583
  /**
2985
3584
  * Produces a deep copy of stylesheet — the instance variables of stylesheet are copied recursively.
2986
3585
  * @param {CSSStyleSheet|CSSOM.CSSStyleSheet} stylesheet