@acemir/cssom 0.9.19 → 0.9.20
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 +448 -98
- package/lib/CSSConditionRule.js +1 -1
- package/lib/CSSContainerRule.js +1 -0
- package/lib/CSSDocumentRule.js +2 -1
- package/lib/CSSGroupingRule.js +2 -1
- package/lib/CSSHostRule.js +3 -2
- package/lib/CSSKeyframesRule.js +2 -1
- package/lib/CSSLayerBlockRule.js +1 -1
- package/lib/CSSMediaRule.js +1 -0
- package/lib/CSSRuleList.js +26 -0
- package/lib/CSSScopeRule.js +53 -0
- package/lib/CSSStartingStyleRule.js +5 -4
- package/lib/CSSStyleRule.js +2 -1
- package/lib/CSSStyleSheet.js +18 -2
- package/lib/CSSSupportsRule.js +1 -0
- package/lib/clone.js +1 -0
- package/lib/index.js +2 -0
- package/lib/parse.js +360 -88
- package/package.json +1 -1
package/build/CSSOM.js
CHANGED
|
@@ -364,6 +364,28 @@ Object.defineProperties(CSSOM.CSSRule.prototype, {
|
|
|
364
364
|
|
|
365
365
|
|
|
366
366
|
|
|
367
|
+
/**
|
|
368
|
+
* @constructor
|
|
369
|
+
* @see https://drafts.csswg.org/cssom/#the-cssrulelist-interface
|
|
370
|
+
*/
|
|
371
|
+
CSSOM.CSSRuleList = function CSSRuleList(){
|
|
372
|
+
const arr = new Array();
|
|
373
|
+
Object.setPrototypeOf(arr, CSSOM.CSSRuleList.prototype);
|
|
374
|
+
return arr;
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
CSSOM.CSSRuleList.prototype = Object.create(Array.prototype);
|
|
378
|
+
CSSOM.CSSRuleList.prototype.constructor = CSSOM.CSSRuleList;
|
|
379
|
+
|
|
380
|
+
CSSOM.CSSRuleList.prototype.item = function(index) {
|
|
381
|
+
return this[index] || null;
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
|
|
367
389
|
/**
|
|
368
390
|
* @constructor
|
|
369
391
|
* @see https://drafts.csswg.org/css-nesting-1/
|
|
@@ -408,7 +430,7 @@ Object.defineProperty(CSSOM.CSSNestedDeclarations.prototype, "cssText", {
|
|
|
408
430
|
*/
|
|
409
431
|
CSSOM.CSSGroupingRule = function CSSGroupingRule() {
|
|
410
432
|
CSSOM.CSSRule.call(this);
|
|
411
|
-
this.cssRules =
|
|
433
|
+
this.cssRules = new CSSOM.CSSRuleList();
|
|
412
434
|
};
|
|
413
435
|
|
|
414
436
|
CSSOM.CSSGroupingRule.prototype = new CSSOM.CSSRule();
|
|
@@ -525,7 +547,6 @@ CSSOM.CSSCounterStyleRule.prototype.type = 11;
|
|
|
525
547
|
*/
|
|
526
548
|
CSSOM.CSSConditionRule = function CSSConditionRule() {
|
|
527
549
|
CSSOM.CSSGroupingRule.call(this);
|
|
528
|
-
this.cssRules = [];
|
|
529
550
|
};
|
|
530
551
|
|
|
531
552
|
CSSOM.CSSConditionRule.prototype = new CSSOM.CSSGroupingRule();
|
|
@@ -1219,7 +1240,7 @@ Object.defineProperty(CSSOM.CSSFontFaceRule.prototype, "cssText", {
|
|
|
1219
1240
|
*/
|
|
1220
1241
|
CSSOM.CSSHostRule = function CSSHostRule() {
|
|
1221
1242
|
CSSOM.CSSRule.call(this);
|
|
1222
|
-
this.cssRules =
|
|
1243
|
+
this.cssRules = new CSSOM.CSSRuleList();
|
|
1223
1244
|
};
|
|
1224
1245
|
|
|
1225
1246
|
CSSOM.CSSHostRule.prototype = new CSSOM.CSSRule();
|
|
@@ -1249,11 +1270,10 @@ Object.defineProperty(CSSOM.CSSHostRule.prototype, "cssText", {
|
|
|
1249
1270
|
* @see http://www.w3.org/TR/shadow-dom/#host-at-rule
|
|
1250
1271
|
*/
|
|
1251
1272
|
CSSOM.CSSStartingStyleRule = function CSSStartingStyleRule() {
|
|
1252
|
-
CSSOM.
|
|
1253
|
-
this.cssRules = [];
|
|
1273
|
+
CSSOM.CSSGroupingRule.call(this);
|
|
1254
1274
|
};
|
|
1255
1275
|
|
|
1256
|
-
CSSOM.CSSStartingStyleRule.prototype = new CSSOM.
|
|
1276
|
+
CSSOM.CSSStartingStyleRule.prototype = new CSSOM.CSSGroupingRule();
|
|
1257
1277
|
CSSOM.CSSStartingStyleRule.prototype.constructor = CSSOM.CSSStartingStyleRule;
|
|
1258
1278
|
CSSOM.CSSStartingStyleRule.prototype.type = 1002;
|
|
1259
1279
|
//FIXME
|
|
@@ -1301,13 +1321,18 @@ Object.defineProperties(CSSOM.StyleSheet.prototype, {
|
|
|
1301
1321
|
*/
|
|
1302
1322
|
CSSOM.CSSStyleSheet = function CSSStyleSheet() {
|
|
1303
1323
|
CSSOM.StyleSheet.call(this);
|
|
1304
|
-
this.cssRules =
|
|
1324
|
+
this.cssRules = new CSSOM.CSSRuleList();
|
|
1305
1325
|
};
|
|
1306
1326
|
|
|
1307
1327
|
|
|
1308
1328
|
CSSOM.CSSStyleSheet.prototype = new CSSOM.StyleSheet();
|
|
1309
1329
|
CSSOM.CSSStyleSheet.prototype.constructor = CSSOM.CSSStyleSheet;
|
|
1310
1330
|
|
|
1331
|
+
Object.defineProperty(CSSOM.CSSStyleSheet.prototype, "rules", {
|
|
1332
|
+
get: function() {
|
|
1333
|
+
return this.cssRules;
|
|
1334
|
+
}
|
|
1335
|
+
});
|
|
1311
1336
|
|
|
1312
1337
|
/**
|
|
1313
1338
|
* Used to insert a new rule into the style sheet. The new rule now becomes part of the cascade.
|
|
@@ -1438,6 +1463,13 @@ CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) {
|
|
|
1438
1463
|
return index;
|
|
1439
1464
|
};
|
|
1440
1465
|
|
|
1466
|
+
CSSOM.CSSStyleSheet.prototype.addRule = function(selector, styleBlock, index) {
|
|
1467
|
+
if (index === void 0) {
|
|
1468
|
+
index = this.cssRules.length;
|
|
1469
|
+
}
|
|
1470
|
+
this.insertRule(selector + "{" + styleBlock + "}", index);
|
|
1471
|
+
return -1;
|
|
1472
|
+
};
|
|
1441
1473
|
|
|
1442
1474
|
/**
|
|
1443
1475
|
* Used to delete a rule from the style sheet.
|
|
@@ -1474,6 +1506,9 @@ CSSOM.CSSStyleSheet.prototype.deleteRule = function(index) {
|
|
|
1474
1506
|
this.cssRules.splice(index, 1);
|
|
1475
1507
|
};
|
|
1476
1508
|
|
|
1509
|
+
CSSOM.CSSStyleSheet.prototype.removeRule = function(index) {
|
|
1510
|
+
this.deleteRule(index);
|
|
1511
|
+
};
|
|
1477
1512
|
|
|
1478
1513
|
/**
|
|
1479
1514
|
* NON-STANDARD
|
|
@@ -1500,7 +1535,7 @@ CSSOM.CSSStyleSheet.prototype.toString = function() {
|
|
|
1500
1535
|
CSSOM.CSSKeyframesRule = function CSSKeyframesRule() {
|
|
1501
1536
|
CSSOM.CSSRule.call(this);
|
|
1502
1537
|
this.name = '';
|
|
1503
|
-
this.cssRules =
|
|
1538
|
+
this.cssRules = new CSSOM.CSSRuleList();
|
|
1504
1539
|
|
|
1505
1540
|
// Set up initial indexed access
|
|
1506
1541
|
this._setupIndexedAccess();
|
|
@@ -1826,7 +1861,7 @@ CSSOM.MatcherList.prototype = {
|
|
|
1826
1861
|
CSSOM.CSSDocumentRule = function CSSDocumentRule() {
|
|
1827
1862
|
CSSOM.CSSRule.call(this);
|
|
1828
1863
|
this.matcher = new CSSOM.MatcherList();
|
|
1829
|
-
this.cssRules =
|
|
1864
|
+
this.cssRules = new CSSOM.CSSRuleList();
|
|
1830
1865
|
};
|
|
1831
1866
|
|
|
1832
1867
|
CSSOM.CSSDocumentRule.prototype = new CSSOM.CSSRule();
|
|
@@ -2227,6 +2262,51 @@ CSSOM.CSSValueExpression.prototype._findMatchedIdx = function(token, idx, sep) {
|
|
|
2227
2262
|
|
|
2228
2263
|
|
|
2229
2264
|
|
|
2265
|
+
/**
|
|
2266
|
+
* @constructor
|
|
2267
|
+
* @see https://drafts.csswg.org/css-cascade-6/#cssscoperule
|
|
2268
|
+
*/
|
|
2269
|
+
CSSOM.CSSScopeRule = function CSSScopeRule() {
|
|
2270
|
+
CSSOM.CSSGroupingRule.call(this);
|
|
2271
|
+
this.__start = null;
|
|
2272
|
+
this.__end = null;
|
|
2273
|
+
};
|
|
2274
|
+
|
|
2275
|
+
CSSOM.CSSScopeRule.prototype = new CSSOM.CSSGroupingRule();
|
|
2276
|
+
CSSOM.CSSScopeRule.prototype.constructor = CSSOM.CSSScopeRule;
|
|
2277
|
+
|
|
2278
|
+
|
|
2279
|
+
Object.defineProperties(CSSOM.CSSScopeRule.prototype, {
|
|
2280
|
+
type: {
|
|
2281
|
+
value: 0,
|
|
2282
|
+
writable: false,
|
|
2283
|
+
},
|
|
2284
|
+
cssText: {
|
|
2285
|
+
get: function () {
|
|
2286
|
+
var cssTexts = [];
|
|
2287
|
+
for (var i = 0, length = this.cssRules.length; i < length; i++) {
|
|
2288
|
+
cssTexts.push(this.cssRules[i].cssText);
|
|
2289
|
+
}
|
|
2290
|
+
return "@scope " + (this.start ? "(" + this.start + ") " : "") + (this.end ? "to (" + this.end + ") " : "") + "{" + (cssTexts.length ? "\n " + cssTexts.join("\n ") : "") + "\n}";
|
|
2291
|
+
},
|
|
2292
|
+
configurable: true,
|
|
2293
|
+
enumerable: true,
|
|
2294
|
+
},
|
|
2295
|
+
start: {
|
|
2296
|
+
get: function () {
|
|
2297
|
+
return this.__start;
|
|
2298
|
+
}
|
|
2299
|
+
},
|
|
2300
|
+
end: {
|
|
2301
|
+
get: function () {
|
|
2302
|
+
return this.__end;
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
});
|
|
2306
|
+
|
|
2307
|
+
|
|
2308
|
+
|
|
2309
|
+
|
|
2230
2310
|
/**
|
|
2231
2311
|
* @constructor
|
|
2232
2312
|
* @see https://drafts.csswg.org/css-cascade-5/#csslayerblockrule
|
|
@@ -2234,7 +2314,6 @@ CSSOM.CSSValueExpression.prototype._findMatchedIdx = function(token, idx, sep) {
|
|
|
2234
2314
|
CSSOM.CSSLayerBlockRule = function CSSLayerBlockRule() {
|
|
2235
2315
|
CSSOM.CSSGroupingRule.call(this);
|
|
2236
2316
|
this.name = "";
|
|
2237
|
-
this.cssRules = [];
|
|
2238
2317
|
};
|
|
2239
2318
|
|
|
2240
2319
|
CSSOM.CSSLayerBlockRule.prototype = new CSSOM.CSSGroupingRule();
|
|
@@ -2332,6 +2411,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2332
2411
|
"conditionBlock": true,
|
|
2333
2412
|
"counterStyleBlock": true,
|
|
2334
2413
|
'documentRule-begin': true,
|
|
2414
|
+
"scopeBlock": true,
|
|
2335
2415
|
"layerBlock": true
|
|
2336
2416
|
};
|
|
2337
2417
|
|
|
@@ -2350,7 +2430,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2350
2430
|
var ancestorRules = [];
|
|
2351
2431
|
var prevScope;
|
|
2352
2432
|
|
|
2353
|
-
var name, priority="", styleRule, mediaRule, containerRule, counterStyleRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule, startingStyleRule, layerBlockRule, layerStatementRule, nestedSelectorRule, namespaceRule;
|
|
2433
|
+
var name, priority="", styleRule, mediaRule, containerRule, counterStyleRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule, startingStyleRule, scopeRule, layerBlockRule, layerStatementRule, nestedSelectorRule, namespaceRule;
|
|
2354
2434
|
|
|
2355
2435
|
// Track defined namespace prefixes for validation
|
|
2356
2436
|
var definedNamespacePrefixes = {};
|
|
@@ -2360,11 +2440,12 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2360
2440
|
// var atRulesStatemenRegExp = /(?<!{.*)[;}]\s*/; // Match a statement by verifying it finds a semicolon or closing brace not followed by another semicolon or closing brace
|
|
2361
2441
|
var beforeRulePortionRegExp = /{(?!.*{)|}(?!.*})|;(?!.*;)|\*\/(?!.*\*\/)/g; // Match the closest allowed character (a opening or closing brace, a semicolon or a comment ending) before the rule
|
|
2362
2442
|
var beforeRuleValidationRegExp = /^[\s{};]*(\*\/\s*)?$/; // Match that the portion before the rule is empty or contains only whitespace, semicolons, opening/closing braces, and optionally a comment ending (*/) followed by whitespace
|
|
2363
|
-
var forwardRuleValidationRegExp = /(?:\(
|
|
2443
|
+
var forwardRuleValidationRegExp = /(?:\s|\/\*|\{|\()/; // Match that the rule is followed by any whitespace, a opening comment, a condition opening parenthesis or a opening brace
|
|
2364
2444
|
var forwardImportRuleValidationRegExp = /(?:\s|\/\*|'|")/; // Match that the rule is followed by any whitespace, an opening comment, a single quote or double quote
|
|
2365
2445
|
var forwardRuleClosingBraceRegExp = /{[^{}]*}|}/; // Finds the next closing brace of a rule block
|
|
2366
2446
|
var forwardRuleSemicolonAndOpeningBraceRegExp = /^.*?({|;)/; // Finds the next semicolon or opening brace after the at-rule
|
|
2367
2447
|
var layerRuleNameRegExp = /^(-?[_a-zA-Z]+(\.[_a-zA-Z]+)*[_a-zA-Z0-9-]*)$/; // Validates a single @layer name
|
|
2448
|
+
var startsWithCombinatorRegExp = /^\s*[>+~]/; // Checks if a selector starts with a CSS combinator (>, +, ~)
|
|
2368
2449
|
|
|
2369
2450
|
/**
|
|
2370
2451
|
* Searches for the first occurrence of a CSS at-rule statement terminator (`;` or `}`)
|
|
@@ -2456,24 +2537,214 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2456
2537
|
return i;
|
|
2457
2538
|
}
|
|
2458
2539
|
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
}
|
|
2472
|
-
|
|
2540
|
+
/**
|
|
2541
|
+
* Parses the scope prelude and extracts start and end selectors.
|
|
2542
|
+
* @param {string} preludeContent - The scope prelude content (without @scope keyword)
|
|
2543
|
+
* @returns {object} Object with startSelector and endSelector properties
|
|
2544
|
+
*/
|
|
2545
|
+
function parseScopePrelude(preludeContent) {
|
|
2546
|
+
var parts = preludeContent.split(/\s*\)\s*to\s+\(/);
|
|
2547
|
+
|
|
2548
|
+
// Restore the parentheses that were consumed by the split
|
|
2549
|
+
if (parts.length === 2) {
|
|
2550
|
+
parts[0] = parts[0] + ')';
|
|
2551
|
+
parts[1] = '(' + parts[1];
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
var hasStart = parts[0] &&
|
|
2555
|
+
parts[0].charAt(0) === '(' &&
|
|
2556
|
+
parts[0].charAt(parts[0].length - 1) === ')';
|
|
2557
|
+
var hasEnd = parts[1] &&
|
|
2558
|
+
parts[1].charAt(0) === '(' &&
|
|
2559
|
+
parts[1].charAt(parts[1].length - 1) === ')';
|
|
2560
|
+
|
|
2561
|
+
// Handle case: @scope to (<end>)
|
|
2562
|
+
var hasOnlyEnd = !hasStart &&
|
|
2563
|
+
!hasEnd &&
|
|
2564
|
+
parts[0].indexOf('to (') === 0 &&
|
|
2565
|
+
parts[0].charAt(parts[0].length - 1) === ')';
|
|
2566
|
+
|
|
2567
|
+
var startSelector = '';
|
|
2568
|
+
var endSelector = '';
|
|
2569
|
+
|
|
2570
|
+
if (hasStart) {
|
|
2571
|
+
startSelector = parts[0].slice(1, -1).trim();
|
|
2572
|
+
}
|
|
2573
|
+
if (hasEnd) {
|
|
2574
|
+
endSelector = parts[1].slice(1, -1).trim();
|
|
2575
|
+
}
|
|
2576
|
+
if (hasOnlyEnd) {
|
|
2577
|
+
endSelector = parts[0].slice(4, -1).trim();
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
return {
|
|
2581
|
+
startSelector: startSelector,
|
|
2582
|
+
endSelector: endSelector,
|
|
2583
|
+
hasStart: hasStart,
|
|
2584
|
+
hasEnd: hasEnd,
|
|
2585
|
+
hasOnlyEnd: hasOnlyEnd
|
|
2586
|
+
};
|
|
2587
|
+
};
|
|
2588
|
+
|
|
2589
|
+
/**
|
|
2590
|
+
* Checks if a selector contains pseudo-elements.
|
|
2591
|
+
* @param {string} selector - The CSS selector to check
|
|
2592
|
+
* @returns {boolean} True if the selector contains pseudo-elements
|
|
2593
|
+
*/
|
|
2594
|
+
function hasPseudoElement(selector) {
|
|
2595
|
+
// Match only double-colon (::) pseudo-elements
|
|
2596
|
+
// Also match legacy single-colon pseudo-elements: :before, :after, :first-line, :first-letter
|
|
2597
|
+
// These must NOT be followed by alphanumeric characters (to avoid matching :before-x or similar)
|
|
2598
|
+
var pseudoElementRegex = /::[a-zA-Z][\w-]*|:(before|after|first-line|first-letter)(?![a-zA-Z0-9_-])/;
|
|
2599
|
+
return pseudoElementRegex.test(selector);
|
|
2600
|
+
};
|
|
2601
|
+
|
|
2602
|
+
/**
|
|
2603
|
+
* Validates balanced parentheses, brackets, and quotes in a selector.
|
|
2604
|
+
*
|
|
2605
|
+
* @param {string} selector - The CSS selector to validate
|
|
2606
|
+
* @param {boolean} trackAttributes - Whether to track attribute selector context
|
|
2607
|
+
* @param {boolean} useStack - Whether to use a stack for parentheses (needed for nested validation)
|
|
2608
|
+
* @returns {boolean} True if the syntax is valid (all brackets, parentheses, and quotes are balanced)
|
|
2609
|
+
*/
|
|
2610
|
+
function validateBalancedSyntax(selector, trackAttributes, useStack) {
|
|
2611
|
+
var parenDepth = 0;
|
|
2612
|
+
var bracketDepth = 0;
|
|
2613
|
+
var inSingleQuote = false;
|
|
2614
|
+
var inDoubleQuote = false;
|
|
2615
|
+
var inAttr = false;
|
|
2616
|
+
var stack = useStack ? [] : null;
|
|
2617
|
+
|
|
2618
|
+
for (var i = 0; i < selector.length; i++) {
|
|
2619
|
+
var char = selector[i];
|
|
2620
|
+
var prevChar = i > 0 ? selector[i - 1] : '';
|
|
2621
|
+
|
|
2622
|
+
if (inSingleQuote) {
|
|
2623
|
+
if (char === "'" && prevChar !== "\\") {
|
|
2624
|
+
inSingleQuote = false;
|
|
2625
|
+
}
|
|
2626
|
+
} else if (inDoubleQuote) {
|
|
2627
|
+
if (char === '"' && prevChar !== "\\") {
|
|
2628
|
+
inDoubleQuote = false;
|
|
2629
|
+
}
|
|
2630
|
+
} else if (trackAttributes && inAttr) {
|
|
2631
|
+
if (char === "]") {
|
|
2632
|
+
inAttr = false;
|
|
2633
|
+
} else if (char === "'") {
|
|
2634
|
+
inSingleQuote = true;
|
|
2635
|
+
} else if (char === '"') {
|
|
2636
|
+
inDoubleQuote = true;
|
|
2637
|
+
}
|
|
2638
|
+
} else {
|
|
2639
|
+
if (trackAttributes && char === "[") {
|
|
2640
|
+
inAttr = true;
|
|
2641
|
+
} else if (char === "'") {
|
|
2642
|
+
inSingleQuote = true;
|
|
2643
|
+
} else if (char === '"') {
|
|
2644
|
+
inDoubleQuote = true;
|
|
2645
|
+
} else if (char === '(') {
|
|
2646
|
+
if (useStack) {
|
|
2647
|
+
stack.push("(");
|
|
2648
|
+
} else {
|
|
2649
|
+
parenDepth++;
|
|
2650
|
+
}
|
|
2651
|
+
} else if (char === ')') {
|
|
2652
|
+
if (useStack) {
|
|
2653
|
+
if (!stack.length || stack.pop() !== "(") {
|
|
2654
|
+
return false;
|
|
2655
|
+
}
|
|
2656
|
+
} else {
|
|
2657
|
+
parenDepth--;
|
|
2658
|
+
if (parenDepth < 0) {
|
|
2659
|
+
return false;
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
} else if (char === '[') {
|
|
2663
|
+
bracketDepth++;
|
|
2664
|
+
} else if (char === ']') {
|
|
2665
|
+
bracketDepth--;
|
|
2666
|
+
if (bracketDepth < 0) {
|
|
2667
|
+
return false;
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
// Check if everything is balanced
|
|
2674
|
+
if (useStack) {
|
|
2675
|
+
return stack.length === 0 && bracketDepth === 0 && !inSingleQuote && !inDoubleQuote && !inAttr;
|
|
2676
|
+
} else {
|
|
2677
|
+
return parenDepth === 0 && bracketDepth === 0 && !inSingleQuote && !inDoubleQuote;
|
|
2473
2678
|
}
|
|
2474
2679
|
};
|
|
2475
2680
|
|
|
2476
|
-
|
|
2681
|
+
/**
|
|
2682
|
+
* Checks for basic syntax errors in selectors (mismatched parentheses, brackets, quotes).
|
|
2683
|
+
* @param {string} selector - The CSS selector to check
|
|
2684
|
+
* @returns {boolean} True if there are syntax errors
|
|
2685
|
+
*/
|
|
2686
|
+
function hasBasicSyntaxError(selector) {
|
|
2687
|
+
return !validateBalancedSyntax(selector, false, false);
|
|
2688
|
+
};
|
|
2689
|
+
|
|
2690
|
+
/**
|
|
2691
|
+
* Checks for invalid combinator patterns in selectors.
|
|
2692
|
+
* @param {string} selector - The CSS selector to check
|
|
2693
|
+
* @returns {boolean} True if the selector contains invalid combinators
|
|
2694
|
+
*/
|
|
2695
|
+
function hasInvalidCombinators(selector) {
|
|
2696
|
+
// Check for invalid combinator patterns:
|
|
2697
|
+
// - <> (not a valid combinator)
|
|
2698
|
+
// - >> (deep descendant combinator, deprecated and invalid)
|
|
2699
|
+
// - Multiple consecutive combinators like >>, >~, etc.
|
|
2700
|
+
if (/<>/.test(selector)) return true;
|
|
2701
|
+
if (/>>/.test(selector)) return true;
|
|
2702
|
+
// Check for other invalid consecutive combinator patterns
|
|
2703
|
+
if (/[>+~]\s*[>+~]/.test(selector)) return true;
|
|
2704
|
+
return false;
|
|
2705
|
+
};
|
|
2706
|
+
|
|
2707
|
+
/**
|
|
2708
|
+
* Checks for invalid pseudo-like syntax (function calls without proper pseudo prefix).
|
|
2709
|
+
* @param {string} selector - The CSS selector to check
|
|
2710
|
+
* @returns {boolean} True if the selector contains invalid pseudo-like syntax
|
|
2711
|
+
*/
|
|
2712
|
+
function hasInvalidPseudoSyntax(selector) {
|
|
2713
|
+
// Check for specific known pseudo-elements used without : or :: prefix
|
|
2714
|
+
// Examples: slotted(div), part(name), cue(selector)
|
|
2715
|
+
// These are ONLY valid as ::slotted(), ::part(), ::cue()
|
|
2716
|
+
var invalidPatterns = [
|
|
2717
|
+
/(?:^|[\s>+~,\[])slotted\s*\(/i,
|
|
2718
|
+
/(?:^|[\s>+~,\[])part\s*\(/i,
|
|
2719
|
+
/(?:^|[\s>+~,\[])cue\s*\(/i,
|
|
2720
|
+
/(?:^|[\s>+~,\[])cue-region\s*\(/i
|
|
2721
|
+
];
|
|
2722
|
+
|
|
2723
|
+
for (var i = 0; i < invalidPatterns.length; i++) {
|
|
2724
|
+
if (invalidPatterns[i].test(selector)) {
|
|
2725
|
+
return true;
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
return false;
|
|
2729
|
+
};
|
|
2730
|
+
|
|
2731
|
+
/**
|
|
2732
|
+
* Checks for invalid nesting selector (&) usage.
|
|
2733
|
+
* The & selector cannot be directly followed by a type selector without a delimiter.
|
|
2734
|
+
* Valid: &.class, &#id, &[attr], &:hover, &::before, & div, &>div
|
|
2735
|
+
* Invalid: &div, &span
|
|
2736
|
+
* @param {string} selector - The CSS selector to check
|
|
2737
|
+
* @returns {boolean} True if the selector contains invalid & usage
|
|
2738
|
+
*/
|
|
2739
|
+
function hasInvalidNestingSelector(selector) {
|
|
2740
|
+
// Check for & followed directly by a letter (type selector) without any delimiter
|
|
2741
|
+
// This regex matches & followed by a letter (start of type selector) that's not preceded by an escape
|
|
2742
|
+
// We need to exclude valid cases like &.class, &#id, &[attr], &:pseudo, &::pseudo, & (with space), &>
|
|
2743
|
+
var invalidNestingPattern = /&(?![.\#\[:>\+~\s])[a-zA-Z]/;
|
|
2744
|
+
return invalidNestingPattern.test(selector);
|
|
2745
|
+
};
|
|
2746
|
+
|
|
2747
|
+
function validateAtRule(atRuleKey, validCallback, cannotBeNested) {
|
|
2477
2748
|
var isValid = false;
|
|
2478
2749
|
var sourceRuleRegExp = atRuleKey === "@import" ? forwardImportRuleValidationRegExp : forwardRuleValidationRegExp;
|
|
2479
2750
|
var ruleRegExp = new RegExp(atRuleKey + sourceRuleRegExp.source, sourceRuleRegExp.flags);
|
|
@@ -2494,6 +2765,56 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2494
2765
|
isValid = true;
|
|
2495
2766
|
}
|
|
2496
2767
|
}
|
|
2768
|
+
|
|
2769
|
+
// Additional validation for @scope rule
|
|
2770
|
+
if (isValid && atRuleKey === "@scope") {
|
|
2771
|
+
var openBraceIndex = ruleSlice.indexOf('{');
|
|
2772
|
+
if (openBraceIndex !== -1) {
|
|
2773
|
+
// Extract the scope prelude (everything between @scope and {)
|
|
2774
|
+
var scopePrelude = ruleSlice.slice(0, openBraceIndex).trim();
|
|
2775
|
+
|
|
2776
|
+
// Skip past '@scope' keyword and whitespace
|
|
2777
|
+
var preludeContent = scopePrelude.slice(6).trim();
|
|
2778
|
+
|
|
2779
|
+
if (preludeContent.length > 0) {
|
|
2780
|
+
// Parse the scope prelude
|
|
2781
|
+
var parsedScopePrelude = parseScopePrelude(preludeContent);
|
|
2782
|
+
var startSelector = parsedScopePrelude.startSelector;
|
|
2783
|
+
var endSelector = parsedScopePrelude.endSelector;
|
|
2784
|
+
var hasStart = parsedScopePrelude.hasStart;
|
|
2785
|
+
var hasEnd = parsedScopePrelude.hasEnd;
|
|
2786
|
+
var hasOnlyEnd = parsedScopePrelude.hasOnlyEnd;
|
|
2787
|
+
|
|
2788
|
+
// Validation rules for @scope:
|
|
2789
|
+
// 1. Empty selectors in parentheses are invalid: @scope () {} or @scope (.a) to () {}
|
|
2790
|
+
if ((hasStart && startSelector === '') || (hasEnd && endSelector === '') || (hasOnlyEnd && endSelector === '')) {
|
|
2791
|
+
isValid = false;
|
|
2792
|
+
}
|
|
2793
|
+
// 2. Pseudo-elements are invalid in scope selectors
|
|
2794
|
+
else if ((startSelector && hasPseudoElement(startSelector)) || (endSelector && hasPseudoElement(endSelector))) {
|
|
2795
|
+
isValid = false;
|
|
2796
|
+
}
|
|
2797
|
+
// 3. Basic syntax errors (mismatched parens, brackets, quotes)
|
|
2798
|
+
else if ((startSelector && hasBasicSyntaxError(startSelector)) || (endSelector && hasBasicSyntaxError(endSelector))) {
|
|
2799
|
+
isValid = false;
|
|
2800
|
+
}
|
|
2801
|
+
// 4. Invalid combinator patterns
|
|
2802
|
+
else if ((startSelector && hasInvalidCombinators(startSelector)) || (endSelector && hasInvalidCombinators(endSelector))) {
|
|
2803
|
+
isValid = false;
|
|
2804
|
+
}
|
|
2805
|
+
// 5. Invalid pseudo-like syntax (function without : or :: prefix)
|
|
2806
|
+
else if ((startSelector && hasInvalidPseudoSyntax(startSelector)) || (endSelector && hasInvalidPseudoSyntax(endSelector))) {
|
|
2807
|
+
isValid = false;
|
|
2808
|
+
}
|
|
2809
|
+
// 6. Invalid structure (no proper parentheses found when prelude is not empty)
|
|
2810
|
+
else if (!hasStart && !hasOnlyEnd) {
|
|
2811
|
+
isValid = false;
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
// Empty prelude (@scope {}) is valid
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2497
2818
|
if (!isValid) {
|
|
2498
2819
|
// If it's invalid the browser will simply ignore the entire invalid block
|
|
2499
2820
|
// Use regex to find the closing brace of the invalid rule
|
|
@@ -2552,52 +2873,23 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2552
2873
|
* @returns {boolean}
|
|
2553
2874
|
*/
|
|
2554
2875
|
function basicSelectorValidator(selector) {
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
var inSingleQuote = false;
|
|
2560
|
-
var inDoubleQuote = false;
|
|
2876
|
+
// Validate balanced syntax with attribute tracking and stack-based parentheses matching
|
|
2877
|
+
if (!validateBalancedSyntax(selector, true, true)) {
|
|
2878
|
+
return false;
|
|
2879
|
+
}
|
|
2561
2880
|
|
|
2562
|
-
|
|
2563
|
-
|
|
2881
|
+
// Check for invalid combinator patterns
|
|
2882
|
+
if (hasInvalidCombinators(selector)) {
|
|
2883
|
+
return false;
|
|
2884
|
+
}
|
|
2564
2885
|
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
}
|
|
2569
|
-
} else if (inDoubleQuote) {
|
|
2570
|
-
if (char === '"' && selector[i - 1] !== "\\") {
|
|
2571
|
-
inDoubleQuote = false;
|
|
2572
|
-
}
|
|
2573
|
-
} else if (inAttr) {
|
|
2574
|
-
if (char === "]") {
|
|
2575
|
-
inAttr = false;
|
|
2576
|
-
} else if (char === "'") {
|
|
2577
|
-
inSingleQuote = true;
|
|
2578
|
-
} else if (char === '"') {
|
|
2579
|
-
inDoubleQuote = true;
|
|
2580
|
-
}
|
|
2581
|
-
} else {
|
|
2582
|
-
if (char === "[") {
|
|
2583
|
-
inAttr = true;
|
|
2584
|
-
} else if (char === "'") {
|
|
2585
|
-
inSingleQuote = true;
|
|
2586
|
-
} else if (char === '"') {
|
|
2587
|
-
inDoubleQuote = true;
|
|
2588
|
-
} else if (char === "(") {
|
|
2589
|
-
stack.push("(");
|
|
2590
|
-
} else if (char === ")") {
|
|
2591
|
-
if (!stack.length || stack.pop() !== "(") {
|
|
2592
|
-
return false;
|
|
2593
|
-
}
|
|
2594
|
-
}
|
|
2595
|
-
}
|
|
2596
|
-
i++;
|
|
2886
|
+
// Check for invalid pseudo-like syntax
|
|
2887
|
+
if (hasInvalidPseudoSyntax(selector)) {
|
|
2888
|
+
return false;
|
|
2597
2889
|
}
|
|
2598
2890
|
|
|
2599
|
-
//
|
|
2600
|
-
if (
|
|
2891
|
+
// Check for invalid nesting selector (&) usage
|
|
2892
|
+
if (hasInvalidNestingSelector(selector)) {
|
|
2601
2893
|
return false;
|
|
2602
2894
|
}
|
|
2603
2895
|
|
|
@@ -2608,7 +2900,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2608
2900
|
var looseSelectorRegExp = /^((?:(?:\*|[a-zA-Z_\u00A0-\uFFFF\\][a-zA-Z0-9_\u00A0-\uFFFF\-\\]*|)\|)?[a-zA-Z_\u00A0-\uFFFF\\][a-zA-Z0-9_\u00A0-\uFFFF\-\\]*|(?:(?:\*|[a-zA-Z_\u00A0-\uFFFF\\][a-zA-Z0-9_\u00A0-\uFFFF\-\\]*|)\|)?\*|#[a-zA-Z0-9_\u00A0-\uFFFF\-\\]+|\.[a-zA-Z0-9_\u00A0-\uFFFF\-\\]+|\[(?:[^\[\]'"]|'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*")*(?:\s+[iI])?\]|::?[a-zA-Z0-9_\u00A0-\uFFFF\-\\]+(?:\((.*)\))?|&|\s*[>+~]\s*|\s+)+$/;
|
|
2609
2901
|
return looseSelectorRegExp.test(selector);
|
|
2610
2902
|
}
|
|
2611
|
-
|
|
2903
|
+
|
|
2612
2904
|
/**
|
|
2613
2905
|
* Regular expression to match CSS pseudo-classes with arguments.
|
|
2614
2906
|
*
|
|
@@ -2639,48 +2931,56 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2639
2931
|
* @returns {string[]} An array of selector parts, split by top-level commas, with whitespace trimmed.
|
|
2640
2932
|
*/
|
|
2641
2933
|
function parseAndSplitNestedSelectors(selector) {
|
|
2642
|
-
var depth = 0;
|
|
2643
|
-
var buffer = "";
|
|
2644
|
-
var parts = [];
|
|
2645
|
-
var inSingleQuote = false;
|
|
2646
|
-
var inDoubleQuote = false;
|
|
2934
|
+
var depth = 0; // Track parenthesis nesting depth
|
|
2935
|
+
var buffer = ""; // Accumulate characters for current selector part
|
|
2936
|
+
var parts = []; // Array of split selector parts
|
|
2937
|
+
var inSingleQuote = false; // Track if we're inside single quotes
|
|
2938
|
+
var inDoubleQuote = false; // Track if we're inside double quotes
|
|
2647
2939
|
var i, char;
|
|
2648
2940
|
|
|
2649
2941
|
for (i = 0; i < selector.length; i++) {
|
|
2650
2942
|
char = selector.charAt(i);
|
|
2651
2943
|
|
|
2944
|
+
// Handle single quote strings
|
|
2652
2945
|
if (char === "'" && !inDoubleQuote) {
|
|
2653
2946
|
inSingleQuote = !inSingleQuote;
|
|
2654
2947
|
buffer += char;
|
|
2655
|
-
}
|
|
2948
|
+
}
|
|
2949
|
+
// Handle double quote strings
|
|
2950
|
+
else if (char === '"' && !inSingleQuote) {
|
|
2656
2951
|
inDoubleQuote = !inDoubleQuote;
|
|
2657
2952
|
buffer += char;
|
|
2658
|
-
}
|
|
2953
|
+
}
|
|
2954
|
+
// Process characters outside of quoted strings
|
|
2955
|
+
else if (!inSingleQuote && !inDoubleQuote) {
|
|
2659
2956
|
if (char === '(') {
|
|
2957
|
+
// Entering a nested level (e.g., :is(...))
|
|
2660
2958
|
depth++;
|
|
2661
2959
|
buffer += char;
|
|
2662
2960
|
} else if (char === ')') {
|
|
2961
|
+
// Exiting a nested level
|
|
2663
2962
|
depth--;
|
|
2664
2963
|
buffer += char;
|
|
2665
|
-
if (depth === 0) {
|
|
2666
|
-
parts.push(buffer.replace(/^\s+|\s+$/g, ""));
|
|
2667
|
-
buffer = "";
|
|
2668
|
-
}
|
|
2669
2964
|
} else if (char === ',' && depth === 0) {
|
|
2670
|
-
|
|
2671
|
-
|
|
2965
|
+
// Found a top-level comma separator - split here
|
|
2966
|
+
if (buffer.trim()) {
|
|
2967
|
+
parts.push(buffer.trim());
|
|
2672
2968
|
}
|
|
2673
2969
|
buffer = "";
|
|
2674
2970
|
} else {
|
|
2971
|
+
// Regular character - add to buffer
|
|
2675
2972
|
buffer += char;
|
|
2676
2973
|
}
|
|
2677
|
-
}
|
|
2974
|
+
}
|
|
2975
|
+
// Characters inside quoted strings - add to buffer
|
|
2976
|
+
else {
|
|
2678
2977
|
buffer += char;
|
|
2679
2978
|
}
|
|
2680
2979
|
}
|
|
2681
2980
|
|
|
2682
|
-
|
|
2683
|
-
|
|
2981
|
+
// Add any remaining content in buffer as the last part
|
|
2982
|
+
if (buffer.trim()) {
|
|
2983
|
+
parts.push(buffer.trim());
|
|
2684
2984
|
}
|
|
2685
2985
|
|
|
2686
2986
|
return parts;
|
|
@@ -2697,8 +2997,8 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2697
2997
|
* @returns {boolean} Returns `true` if the selector is valid, otherwise `false`.
|
|
2698
2998
|
*/
|
|
2699
2999
|
|
|
2700
|
-
// Cache to store validated selectors
|
|
2701
|
-
var validatedSelectorsCache =
|
|
3000
|
+
// Cache to store validated selectors (ES5-compliant object)
|
|
3001
|
+
var validatedSelectorsCache = {};
|
|
2702
3002
|
|
|
2703
3003
|
// Only pseudo-classes that accept selector lists should recurse
|
|
2704
3004
|
var selectorListPseudoClasses = {
|
|
@@ -2709,8 +3009,8 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2709
3009
|
};
|
|
2710
3010
|
|
|
2711
3011
|
function validateSelector(selector) {
|
|
2712
|
-
if (validatedSelectorsCache.
|
|
2713
|
-
return validatedSelectorsCache
|
|
3012
|
+
if (validatedSelectorsCache.hasOwnProperty(selector)) {
|
|
3013
|
+
return validatedSelectorsCache[selector];
|
|
2714
3014
|
}
|
|
2715
3015
|
|
|
2716
3016
|
// Use a non-global regex to find all pseudo-classes with arguments
|
|
@@ -2727,15 +3027,15 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2727
3027
|
var nestedSelectors = parseAndSplitNestedSelectors(pseudoClassMatches[j][2]);
|
|
2728
3028
|
for (var i = 0; i < nestedSelectors.length; i++) {
|
|
2729
3029
|
var nestedSelector = nestedSelectors[i];
|
|
2730
|
-
if (!validatedSelectorsCache.
|
|
3030
|
+
if (!validatedSelectorsCache.hasOwnProperty(nestedSelector)) {
|
|
2731
3031
|
var nestedSelectorValidation = validateSelector(nestedSelector);
|
|
2732
|
-
validatedSelectorsCache
|
|
3032
|
+
validatedSelectorsCache[nestedSelector] = nestedSelectorValidation;
|
|
2733
3033
|
if (!nestedSelectorValidation) {
|
|
2734
|
-
validatedSelectorsCache
|
|
3034
|
+
validatedSelectorsCache[selector] = false;
|
|
2735
3035
|
return false;
|
|
2736
3036
|
}
|
|
2737
|
-
} else if (!validatedSelectorsCache
|
|
2738
|
-
validatedSelectorsCache
|
|
3037
|
+
} else if (!validatedSelectorsCache[nestedSelector]) {
|
|
3038
|
+
validatedSelectorsCache[selector] = false;
|
|
2739
3039
|
return false;
|
|
2740
3040
|
}
|
|
2741
3041
|
}
|
|
@@ -2743,7 +3043,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2743
3043
|
}
|
|
2744
3044
|
|
|
2745
3045
|
var basicSelectorValidation = basicSelectorValidator(selector);
|
|
2746
|
-
validatedSelectorsCache
|
|
3046
|
+
validatedSelectorsCache[selector] = basicSelectorValidation;
|
|
2747
3047
|
|
|
2748
3048
|
return basicSelectorValidation;
|
|
2749
3049
|
}
|
|
@@ -2835,6 +3135,23 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2835
3135
|
return true;
|
|
2836
3136
|
}
|
|
2837
3137
|
|
|
3138
|
+
function parseError(message) {
|
|
3139
|
+
var lines = token.substring(0, i).split('\n');
|
|
3140
|
+
var lineCount = lines.length;
|
|
3141
|
+
var charCount = lines.pop().length + 1;
|
|
3142
|
+
var error = new Error(message + ' (line ' + lineCount + ', char ' + charCount + ')');
|
|
3143
|
+
error.line = lineCount;
|
|
3144
|
+
/* jshint sub : true */
|
|
3145
|
+
error['char'] = charCount;
|
|
3146
|
+
error.styleSheet = styleSheet;
|
|
3147
|
+
// Print the error but continue parsing the sheet
|
|
3148
|
+
try {
|
|
3149
|
+
throw error;
|
|
3150
|
+
} catch(e) {
|
|
3151
|
+
errorHandler && errorHandler(e);
|
|
3152
|
+
}
|
|
3153
|
+
};
|
|
3154
|
+
|
|
2838
3155
|
var endingIndex = token.length - 1;
|
|
2839
3156
|
|
|
2840
3157
|
for (var character; (character = token.charAt(i)); i++) {
|
|
@@ -2979,6 +3296,15 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2979
3296
|
}, true);
|
|
2980
3297
|
buffer = "";
|
|
2981
3298
|
break;
|
|
3299
|
+
} else if (token.indexOf("@scope", i) === i) {
|
|
3300
|
+
validateAtRule("@scope", function(){
|
|
3301
|
+
state = "scopeBlock";
|
|
3302
|
+
scopeRule = new CSSOM.CSSScopeRule();
|
|
3303
|
+
scopeRule.__starts = i;
|
|
3304
|
+
i += "scope".length;
|
|
3305
|
+
});
|
|
3306
|
+
buffer = "";
|
|
3307
|
+
break;
|
|
2982
3308
|
} else if (token.indexOf("@layer", i) === i) {
|
|
2983
3309
|
validateAtRule("@layer", function(){
|
|
2984
3310
|
state = "layerBlock"
|
|
@@ -3134,6 +3460,27 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3134
3460
|
supportsRule.__parentStyleSheet = styleSheet;
|
|
3135
3461
|
buffer = "";
|
|
3136
3462
|
state = "before-selector";
|
|
3463
|
+
} else if (state === "scopeBlock") {
|
|
3464
|
+
var parsedScopePrelude = parseScopePrelude(buffer.trim());
|
|
3465
|
+
|
|
3466
|
+
if (parsedScopePrelude.hasStart) {
|
|
3467
|
+
scopeRule.__start = parsedScopePrelude.startSelector;
|
|
3468
|
+
}
|
|
3469
|
+
if (parsedScopePrelude.hasEnd) {
|
|
3470
|
+
scopeRule.__end = parsedScopePrelude.endSelector;
|
|
3471
|
+
}
|
|
3472
|
+
if (parsedScopePrelude.hasOnlyEnd) {
|
|
3473
|
+
scopeRule.__end = parsedScopePrelude.endSelector;
|
|
3474
|
+
}
|
|
3475
|
+
|
|
3476
|
+
if (parentRule) {
|
|
3477
|
+
scopeRule.__parentRule = parentRule;
|
|
3478
|
+
ancestorRules.push(parentRule);
|
|
3479
|
+
}
|
|
3480
|
+
currentScope = parentRule = scopeRule;
|
|
3481
|
+
scopeRule.__parentStyleSheet = styleSheet;
|
|
3482
|
+
buffer = "";
|
|
3483
|
+
state = "before-selector";
|
|
3137
3484
|
} else if (state === "layerBlock") {
|
|
3138
3485
|
layerBlockRule.name = buffer.trim();
|
|
3139
3486
|
|
|
@@ -3232,8 +3579,9 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3232
3579
|
styleRule.selectorText = processedSelectorText;
|
|
3233
3580
|
} else {
|
|
3234
3581
|
styleRule.selectorText = parseAndSplitNestedSelectors(processedSelectorText).map(function(sel) {
|
|
3235
|
-
|
|
3236
|
-
|
|
3582
|
+
// Add & at the beginning if there's no & in the selector, or if it starts with a combinator
|
|
3583
|
+
return (sel.indexOf('&') === -1 || startsWithCombinatorRegExp.test(sel)) ? '& ' + sel : sel;
|
|
3584
|
+
}).join(', ');
|
|
3237
3585
|
}
|
|
3238
3586
|
styleRule.style.__starts = i - buffer.length;
|
|
3239
3587
|
styleRule.__parentRule = parentRule;
|
|
@@ -3469,6 +3817,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3469
3817
|
|| parentRule.constructor.name === "CSSMediaRule"
|
|
3470
3818
|
|| parentRule.constructor.name === "CSSSupportsRule"
|
|
3471
3819
|
|| parentRule.constructor.name === "CSSContainerRule"
|
|
3820
|
+
|| parentRule.constructor.name === "CSSScopeRule"
|
|
3472
3821
|
|| parentRule.constructor.name === "CSSLayerBlockRule"
|
|
3473
3822
|
|| parentRule.constructor.name === "CSSStartingStyleRule"
|
|
3474
3823
|
) {
|
|
@@ -3538,6 +3887,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3538
3887
|
|| parentRule.constructor.name === "CSSMediaRule"
|
|
3539
3888
|
|| parentRule.constructor.name === "CSSSupportsRule"
|
|
3540
3889
|
|| parentRule.constructor.name === "CSSContainerRule"
|
|
3890
|
+
|| parentRule.constructor.name === "CSSScopeRule"
|
|
3541
3891
|
|| parentRule.constructor.name === "CSSLayerBlockRule"
|
|
3542
3892
|
|| parentRule.constructor.name === "CSSStartingStyleRule"
|
|
3543
3893
|
) {
|