@acemir/cssom 0.9.18 → 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 +470 -99
- 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/CSSRule.js +22 -1
- 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
|
@@ -301,6 +301,27 @@ CSSOM.CSSRule = function CSSRule() {
|
|
|
301
301
|
this.__parentStyleSheet = null;
|
|
302
302
|
};
|
|
303
303
|
|
|
304
|
+
CSSOM.CSSRule.UNKNOWN_RULE = 0; // obsolete
|
|
305
|
+
CSSOM.CSSRule.STYLE_RULE = 1;
|
|
306
|
+
CSSOM.CSSRule.CHARSET_RULE = 2; // obsolete
|
|
307
|
+
CSSOM.CSSRule.IMPORT_RULE = 3;
|
|
308
|
+
CSSOM.CSSRule.MEDIA_RULE = 4;
|
|
309
|
+
CSSOM.CSSRule.FONT_FACE_RULE = 5;
|
|
310
|
+
CSSOM.CSSRule.PAGE_RULE = 6;
|
|
311
|
+
CSSOM.CSSRule.KEYFRAMES_RULE = 7;
|
|
312
|
+
CSSOM.CSSRule.KEYFRAME_RULE = 8;
|
|
313
|
+
CSSOM.CSSRule.MARGIN_RULE = 9;
|
|
314
|
+
CSSOM.CSSRule.NAMESPACE_RULE = 10;
|
|
315
|
+
CSSOM.CSSRule.COUNTER_STYLE_RULE = 11;
|
|
316
|
+
CSSOM.CSSRule.SUPPORTS_RULE = 12;
|
|
317
|
+
CSSOM.CSSRule.DOCUMENT_RULE = 13;
|
|
318
|
+
CSSOM.CSSRule.FONT_FEATURE_VALUES_RULE = 14;
|
|
319
|
+
CSSOM.CSSRule.VIEWPORT_RULE = 15;
|
|
320
|
+
CSSOM.CSSRule.REGION_STYLE_RULE = 16;
|
|
321
|
+
CSSOM.CSSRule.CONTAINER_RULE = 17;
|
|
322
|
+
CSSOM.CSSRule.LAYER_BLOCK_RULE = 18;
|
|
323
|
+
CSSOM.CSSRule.STARTING_STYLE_RULE = 1002;
|
|
324
|
+
|
|
304
325
|
Object.defineProperties(CSSOM.CSSRule.prototype, {
|
|
305
326
|
|
|
306
327
|
constructor: { value: CSSOM.CSSRule },
|
|
@@ -337,7 +358,29 @@ Object.defineProperties(CSSOM.CSSRule.prototype, {
|
|
|
337
358
|
CONTAINER_RULE: { value: 17, enumerable: true },
|
|
338
359
|
LAYER_BLOCK_RULE: { value: 18, enumerable: true },
|
|
339
360
|
STARTING_STYLE_RULE: { value: 1002, enumerable: true },
|
|
340
|
-
})
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
|
|
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
|
+
|
|
341
384
|
|
|
342
385
|
|
|
343
386
|
|
|
@@ -387,7 +430,7 @@ Object.defineProperty(CSSOM.CSSNestedDeclarations.prototype, "cssText", {
|
|
|
387
430
|
*/
|
|
388
431
|
CSSOM.CSSGroupingRule = function CSSGroupingRule() {
|
|
389
432
|
CSSOM.CSSRule.call(this);
|
|
390
|
-
this.cssRules =
|
|
433
|
+
this.cssRules = new CSSOM.CSSRuleList();
|
|
391
434
|
};
|
|
392
435
|
|
|
393
436
|
CSSOM.CSSGroupingRule.prototype = new CSSOM.CSSRule();
|
|
@@ -504,7 +547,6 @@ CSSOM.CSSCounterStyleRule.prototype.type = 11;
|
|
|
504
547
|
*/
|
|
505
548
|
CSSOM.CSSConditionRule = function CSSConditionRule() {
|
|
506
549
|
CSSOM.CSSGroupingRule.call(this);
|
|
507
|
-
this.cssRules = [];
|
|
508
550
|
};
|
|
509
551
|
|
|
510
552
|
CSSOM.CSSConditionRule.prototype = new CSSOM.CSSGroupingRule();
|
|
@@ -1198,7 +1240,7 @@ Object.defineProperty(CSSOM.CSSFontFaceRule.prototype, "cssText", {
|
|
|
1198
1240
|
*/
|
|
1199
1241
|
CSSOM.CSSHostRule = function CSSHostRule() {
|
|
1200
1242
|
CSSOM.CSSRule.call(this);
|
|
1201
|
-
this.cssRules =
|
|
1243
|
+
this.cssRules = new CSSOM.CSSRuleList();
|
|
1202
1244
|
};
|
|
1203
1245
|
|
|
1204
1246
|
CSSOM.CSSHostRule.prototype = new CSSOM.CSSRule();
|
|
@@ -1228,11 +1270,10 @@ Object.defineProperty(CSSOM.CSSHostRule.prototype, "cssText", {
|
|
|
1228
1270
|
* @see http://www.w3.org/TR/shadow-dom/#host-at-rule
|
|
1229
1271
|
*/
|
|
1230
1272
|
CSSOM.CSSStartingStyleRule = function CSSStartingStyleRule() {
|
|
1231
|
-
CSSOM.
|
|
1232
|
-
this.cssRules = [];
|
|
1273
|
+
CSSOM.CSSGroupingRule.call(this);
|
|
1233
1274
|
};
|
|
1234
1275
|
|
|
1235
|
-
CSSOM.CSSStartingStyleRule.prototype = new CSSOM.
|
|
1276
|
+
CSSOM.CSSStartingStyleRule.prototype = new CSSOM.CSSGroupingRule();
|
|
1236
1277
|
CSSOM.CSSStartingStyleRule.prototype.constructor = CSSOM.CSSStartingStyleRule;
|
|
1237
1278
|
CSSOM.CSSStartingStyleRule.prototype.type = 1002;
|
|
1238
1279
|
//FIXME
|
|
@@ -1280,13 +1321,18 @@ Object.defineProperties(CSSOM.StyleSheet.prototype, {
|
|
|
1280
1321
|
*/
|
|
1281
1322
|
CSSOM.CSSStyleSheet = function CSSStyleSheet() {
|
|
1282
1323
|
CSSOM.StyleSheet.call(this);
|
|
1283
|
-
this.cssRules =
|
|
1324
|
+
this.cssRules = new CSSOM.CSSRuleList();
|
|
1284
1325
|
};
|
|
1285
1326
|
|
|
1286
1327
|
|
|
1287
1328
|
CSSOM.CSSStyleSheet.prototype = new CSSOM.StyleSheet();
|
|
1288
1329
|
CSSOM.CSSStyleSheet.prototype.constructor = CSSOM.CSSStyleSheet;
|
|
1289
1330
|
|
|
1331
|
+
Object.defineProperty(CSSOM.CSSStyleSheet.prototype, "rules", {
|
|
1332
|
+
get: function() {
|
|
1333
|
+
return this.cssRules;
|
|
1334
|
+
}
|
|
1335
|
+
});
|
|
1290
1336
|
|
|
1291
1337
|
/**
|
|
1292
1338
|
* Used to insert a new rule into the style sheet. The new rule now becomes part of the cascade.
|
|
@@ -1417,6 +1463,13 @@ CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) {
|
|
|
1417
1463
|
return index;
|
|
1418
1464
|
};
|
|
1419
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
|
+
};
|
|
1420
1473
|
|
|
1421
1474
|
/**
|
|
1422
1475
|
* Used to delete a rule from the style sheet.
|
|
@@ -1453,6 +1506,9 @@ CSSOM.CSSStyleSheet.prototype.deleteRule = function(index) {
|
|
|
1453
1506
|
this.cssRules.splice(index, 1);
|
|
1454
1507
|
};
|
|
1455
1508
|
|
|
1509
|
+
CSSOM.CSSStyleSheet.prototype.removeRule = function(index) {
|
|
1510
|
+
this.deleteRule(index);
|
|
1511
|
+
};
|
|
1456
1512
|
|
|
1457
1513
|
/**
|
|
1458
1514
|
* NON-STANDARD
|
|
@@ -1479,7 +1535,7 @@ CSSOM.CSSStyleSheet.prototype.toString = function() {
|
|
|
1479
1535
|
CSSOM.CSSKeyframesRule = function CSSKeyframesRule() {
|
|
1480
1536
|
CSSOM.CSSRule.call(this);
|
|
1481
1537
|
this.name = '';
|
|
1482
|
-
this.cssRules =
|
|
1538
|
+
this.cssRules = new CSSOM.CSSRuleList();
|
|
1483
1539
|
|
|
1484
1540
|
// Set up initial indexed access
|
|
1485
1541
|
this._setupIndexedAccess();
|
|
@@ -1805,7 +1861,7 @@ CSSOM.MatcherList.prototype = {
|
|
|
1805
1861
|
CSSOM.CSSDocumentRule = function CSSDocumentRule() {
|
|
1806
1862
|
CSSOM.CSSRule.call(this);
|
|
1807
1863
|
this.matcher = new CSSOM.MatcherList();
|
|
1808
|
-
this.cssRules =
|
|
1864
|
+
this.cssRules = new CSSOM.CSSRuleList();
|
|
1809
1865
|
};
|
|
1810
1866
|
|
|
1811
1867
|
CSSOM.CSSDocumentRule.prototype = new CSSOM.CSSRule();
|
|
@@ -2206,6 +2262,51 @@ CSSOM.CSSValueExpression.prototype._findMatchedIdx = function(token, idx, sep) {
|
|
|
2206
2262
|
|
|
2207
2263
|
|
|
2208
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
|
+
|
|
2209
2310
|
/**
|
|
2210
2311
|
* @constructor
|
|
2211
2312
|
* @see https://drafts.csswg.org/css-cascade-5/#csslayerblockrule
|
|
@@ -2213,7 +2314,6 @@ CSSOM.CSSValueExpression.prototype._findMatchedIdx = function(token, idx, sep) {
|
|
|
2213
2314
|
CSSOM.CSSLayerBlockRule = function CSSLayerBlockRule() {
|
|
2214
2315
|
CSSOM.CSSGroupingRule.call(this);
|
|
2215
2316
|
this.name = "";
|
|
2216
|
-
this.cssRules = [];
|
|
2217
2317
|
};
|
|
2218
2318
|
|
|
2219
2319
|
CSSOM.CSSLayerBlockRule.prototype = new CSSOM.CSSGroupingRule();
|
|
@@ -2311,6 +2411,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2311
2411
|
"conditionBlock": true,
|
|
2312
2412
|
"counterStyleBlock": true,
|
|
2313
2413
|
'documentRule-begin': true,
|
|
2414
|
+
"scopeBlock": true,
|
|
2314
2415
|
"layerBlock": true
|
|
2315
2416
|
};
|
|
2316
2417
|
|
|
@@ -2329,7 +2430,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2329
2430
|
var ancestorRules = [];
|
|
2330
2431
|
var prevScope;
|
|
2331
2432
|
|
|
2332
|
-
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;
|
|
2333
2434
|
|
|
2334
2435
|
// Track defined namespace prefixes for validation
|
|
2335
2436
|
var definedNamespacePrefixes = {};
|
|
@@ -2339,11 +2440,12 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2339
2440
|
// var atRulesStatemenRegExp = /(?<!{.*)[;}]\s*/; // Match a statement by verifying it finds a semicolon or closing brace not followed by another semicolon or closing brace
|
|
2340
2441
|
var beforeRulePortionRegExp = /{(?!.*{)|}(?!.*})|;(?!.*;)|\*\/(?!.*\*\/)/g; // Match the closest allowed character (a opening or closing brace, a semicolon or a comment ending) before the rule
|
|
2341
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
|
|
2342
|
-
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
|
|
2343
2444
|
var forwardImportRuleValidationRegExp = /(?:\s|\/\*|'|")/; // Match that the rule is followed by any whitespace, an opening comment, a single quote or double quote
|
|
2344
2445
|
var forwardRuleClosingBraceRegExp = /{[^{}]*}|}/; // Finds the next closing brace of a rule block
|
|
2345
2446
|
var forwardRuleSemicolonAndOpeningBraceRegExp = /^.*?({|;)/; // Finds the next semicolon or opening brace after the at-rule
|
|
2346
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 (>, +, ~)
|
|
2347
2449
|
|
|
2348
2450
|
/**
|
|
2349
2451
|
* Searches for the first occurrence of a CSS at-rule statement terminator (`;` or `}`)
|
|
@@ -2435,24 +2537,214 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2435
2537
|
return i;
|
|
2436
2538
|
}
|
|
2437
2539
|
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
}
|
|
2451
|
-
|
|
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
|
+
}
|
|
2452
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;
|
|
2678
|
+
}
|
|
2679
|
+
};
|
|
2680
|
+
|
|
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);
|
|
2453
2688
|
};
|
|
2454
2689
|
|
|
2455
|
-
|
|
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) {
|
|
2456
2748
|
var isValid = false;
|
|
2457
2749
|
var sourceRuleRegExp = atRuleKey === "@import" ? forwardImportRuleValidationRegExp : forwardRuleValidationRegExp;
|
|
2458
2750
|
var ruleRegExp = new RegExp(atRuleKey + sourceRuleRegExp.source, sourceRuleRegExp.flags);
|
|
@@ -2473,6 +2765,56 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2473
2765
|
isValid = true;
|
|
2474
2766
|
}
|
|
2475
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
|
+
|
|
2476
2818
|
if (!isValid) {
|
|
2477
2819
|
// If it's invalid the browser will simply ignore the entire invalid block
|
|
2478
2820
|
// Use regex to find the closing brace of the invalid rule
|
|
@@ -2531,52 +2873,23 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2531
2873
|
* @returns {boolean}
|
|
2532
2874
|
*/
|
|
2533
2875
|
function basicSelectorValidator(selector) {
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
var inSingleQuote = false;
|
|
2539
|
-
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
|
+
}
|
|
2540
2880
|
|
|
2541
|
-
|
|
2542
|
-
|
|
2881
|
+
// Check for invalid combinator patterns
|
|
2882
|
+
if (hasInvalidCombinators(selector)) {
|
|
2883
|
+
return false;
|
|
2884
|
+
}
|
|
2543
2885
|
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
}
|
|
2548
|
-
} else if (inDoubleQuote) {
|
|
2549
|
-
if (char === '"' && selector[i - 1] !== "\\") {
|
|
2550
|
-
inDoubleQuote = false;
|
|
2551
|
-
}
|
|
2552
|
-
} else if (inAttr) {
|
|
2553
|
-
if (char === "]") {
|
|
2554
|
-
inAttr = false;
|
|
2555
|
-
} else if (char === "'") {
|
|
2556
|
-
inSingleQuote = true;
|
|
2557
|
-
} else if (char === '"') {
|
|
2558
|
-
inDoubleQuote = true;
|
|
2559
|
-
}
|
|
2560
|
-
} else {
|
|
2561
|
-
if (char === "[") {
|
|
2562
|
-
inAttr = true;
|
|
2563
|
-
} else if (char === "'") {
|
|
2564
|
-
inSingleQuote = true;
|
|
2565
|
-
} else if (char === '"') {
|
|
2566
|
-
inDoubleQuote = true;
|
|
2567
|
-
} else if (char === "(") {
|
|
2568
|
-
stack.push("(");
|
|
2569
|
-
} else if (char === ")") {
|
|
2570
|
-
if (!stack.length || stack.pop() !== "(") {
|
|
2571
|
-
return false;
|
|
2572
|
-
}
|
|
2573
|
-
}
|
|
2574
|
-
}
|
|
2575
|
-
i++;
|
|
2886
|
+
// Check for invalid pseudo-like syntax
|
|
2887
|
+
if (hasInvalidPseudoSyntax(selector)) {
|
|
2888
|
+
return false;
|
|
2576
2889
|
}
|
|
2577
2890
|
|
|
2578
|
-
//
|
|
2579
|
-
if (
|
|
2891
|
+
// Check for invalid nesting selector (&) usage
|
|
2892
|
+
if (hasInvalidNestingSelector(selector)) {
|
|
2580
2893
|
return false;
|
|
2581
2894
|
}
|
|
2582
2895
|
|
|
@@ -2587,7 +2900,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2587
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+)+$/;
|
|
2588
2901
|
return looseSelectorRegExp.test(selector);
|
|
2589
2902
|
}
|
|
2590
|
-
|
|
2903
|
+
|
|
2591
2904
|
/**
|
|
2592
2905
|
* Regular expression to match CSS pseudo-classes with arguments.
|
|
2593
2906
|
*
|
|
@@ -2618,48 +2931,56 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2618
2931
|
* @returns {string[]} An array of selector parts, split by top-level commas, with whitespace trimmed.
|
|
2619
2932
|
*/
|
|
2620
2933
|
function parseAndSplitNestedSelectors(selector) {
|
|
2621
|
-
var depth = 0;
|
|
2622
|
-
var buffer = "";
|
|
2623
|
-
var parts = [];
|
|
2624
|
-
var inSingleQuote = false;
|
|
2625
|
-
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
|
|
2626
2939
|
var i, char;
|
|
2627
2940
|
|
|
2628
2941
|
for (i = 0; i < selector.length; i++) {
|
|
2629
2942
|
char = selector.charAt(i);
|
|
2630
2943
|
|
|
2944
|
+
// Handle single quote strings
|
|
2631
2945
|
if (char === "'" && !inDoubleQuote) {
|
|
2632
2946
|
inSingleQuote = !inSingleQuote;
|
|
2633
2947
|
buffer += char;
|
|
2634
|
-
}
|
|
2948
|
+
}
|
|
2949
|
+
// Handle double quote strings
|
|
2950
|
+
else if (char === '"' && !inSingleQuote) {
|
|
2635
2951
|
inDoubleQuote = !inDoubleQuote;
|
|
2636
2952
|
buffer += char;
|
|
2637
|
-
}
|
|
2953
|
+
}
|
|
2954
|
+
// Process characters outside of quoted strings
|
|
2955
|
+
else if (!inSingleQuote && !inDoubleQuote) {
|
|
2638
2956
|
if (char === '(') {
|
|
2957
|
+
// Entering a nested level (e.g., :is(...))
|
|
2639
2958
|
depth++;
|
|
2640
2959
|
buffer += char;
|
|
2641
2960
|
} else if (char === ')') {
|
|
2961
|
+
// Exiting a nested level
|
|
2642
2962
|
depth--;
|
|
2643
2963
|
buffer += char;
|
|
2644
|
-
if (depth === 0) {
|
|
2645
|
-
parts.push(buffer.replace(/^\s+|\s+$/g, ""));
|
|
2646
|
-
buffer = "";
|
|
2647
|
-
}
|
|
2648
2964
|
} else if (char === ',' && depth === 0) {
|
|
2649
|
-
|
|
2650
|
-
|
|
2965
|
+
// Found a top-level comma separator - split here
|
|
2966
|
+
if (buffer.trim()) {
|
|
2967
|
+
parts.push(buffer.trim());
|
|
2651
2968
|
}
|
|
2652
2969
|
buffer = "";
|
|
2653
2970
|
} else {
|
|
2971
|
+
// Regular character - add to buffer
|
|
2654
2972
|
buffer += char;
|
|
2655
2973
|
}
|
|
2656
|
-
}
|
|
2974
|
+
}
|
|
2975
|
+
// Characters inside quoted strings - add to buffer
|
|
2976
|
+
else {
|
|
2657
2977
|
buffer += char;
|
|
2658
2978
|
}
|
|
2659
2979
|
}
|
|
2660
2980
|
|
|
2661
|
-
|
|
2662
|
-
|
|
2981
|
+
// Add any remaining content in buffer as the last part
|
|
2982
|
+
if (buffer.trim()) {
|
|
2983
|
+
parts.push(buffer.trim());
|
|
2663
2984
|
}
|
|
2664
2985
|
|
|
2665
2986
|
return parts;
|
|
@@ -2676,8 +2997,8 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2676
2997
|
* @returns {boolean} Returns `true` if the selector is valid, otherwise `false`.
|
|
2677
2998
|
*/
|
|
2678
2999
|
|
|
2679
|
-
// Cache to store validated selectors
|
|
2680
|
-
var validatedSelectorsCache =
|
|
3000
|
+
// Cache to store validated selectors (ES5-compliant object)
|
|
3001
|
+
var validatedSelectorsCache = {};
|
|
2681
3002
|
|
|
2682
3003
|
// Only pseudo-classes that accept selector lists should recurse
|
|
2683
3004
|
var selectorListPseudoClasses = {
|
|
@@ -2688,8 +3009,8 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2688
3009
|
};
|
|
2689
3010
|
|
|
2690
3011
|
function validateSelector(selector) {
|
|
2691
|
-
if (validatedSelectorsCache.
|
|
2692
|
-
return validatedSelectorsCache
|
|
3012
|
+
if (validatedSelectorsCache.hasOwnProperty(selector)) {
|
|
3013
|
+
return validatedSelectorsCache[selector];
|
|
2693
3014
|
}
|
|
2694
3015
|
|
|
2695
3016
|
// Use a non-global regex to find all pseudo-classes with arguments
|
|
@@ -2706,15 +3027,15 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2706
3027
|
var nestedSelectors = parseAndSplitNestedSelectors(pseudoClassMatches[j][2]);
|
|
2707
3028
|
for (var i = 0; i < nestedSelectors.length; i++) {
|
|
2708
3029
|
var nestedSelector = nestedSelectors[i];
|
|
2709
|
-
if (!validatedSelectorsCache.
|
|
3030
|
+
if (!validatedSelectorsCache.hasOwnProperty(nestedSelector)) {
|
|
2710
3031
|
var nestedSelectorValidation = validateSelector(nestedSelector);
|
|
2711
|
-
validatedSelectorsCache
|
|
3032
|
+
validatedSelectorsCache[nestedSelector] = nestedSelectorValidation;
|
|
2712
3033
|
if (!nestedSelectorValidation) {
|
|
2713
|
-
validatedSelectorsCache
|
|
3034
|
+
validatedSelectorsCache[selector] = false;
|
|
2714
3035
|
return false;
|
|
2715
3036
|
}
|
|
2716
|
-
} else if (!validatedSelectorsCache
|
|
2717
|
-
validatedSelectorsCache
|
|
3037
|
+
} else if (!validatedSelectorsCache[nestedSelector]) {
|
|
3038
|
+
validatedSelectorsCache[selector] = false;
|
|
2718
3039
|
return false;
|
|
2719
3040
|
}
|
|
2720
3041
|
}
|
|
@@ -2722,7 +3043,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2722
3043
|
}
|
|
2723
3044
|
|
|
2724
3045
|
var basicSelectorValidation = basicSelectorValidator(selector);
|
|
2725
|
-
validatedSelectorsCache
|
|
3046
|
+
validatedSelectorsCache[selector] = basicSelectorValidation;
|
|
2726
3047
|
|
|
2727
3048
|
return basicSelectorValidation;
|
|
2728
3049
|
}
|
|
@@ -2814,6 +3135,23 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2814
3135
|
return true;
|
|
2815
3136
|
}
|
|
2816
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
|
+
|
|
2817
3155
|
var endingIndex = token.length - 1;
|
|
2818
3156
|
|
|
2819
3157
|
for (var character; (character = token.charAt(i)); i++) {
|
|
@@ -2958,6 +3296,15 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2958
3296
|
}, true);
|
|
2959
3297
|
buffer = "";
|
|
2960
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;
|
|
2961
3308
|
} else if (token.indexOf("@layer", i) === i) {
|
|
2962
3309
|
validateAtRule("@layer", function(){
|
|
2963
3310
|
state = "layerBlock"
|
|
@@ -3113,6 +3460,27 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3113
3460
|
supportsRule.__parentStyleSheet = styleSheet;
|
|
3114
3461
|
buffer = "";
|
|
3115
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";
|
|
3116
3484
|
} else if (state === "layerBlock") {
|
|
3117
3485
|
layerBlockRule.name = buffer.trim();
|
|
3118
3486
|
|
|
@@ -3211,8 +3579,9 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3211
3579
|
styleRule.selectorText = processedSelectorText;
|
|
3212
3580
|
} else {
|
|
3213
3581
|
styleRule.selectorText = parseAndSplitNestedSelectors(processedSelectorText).map(function(sel) {
|
|
3214
|
-
|
|
3215
|
-
|
|
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(', ');
|
|
3216
3585
|
}
|
|
3217
3586
|
styleRule.style.__starts = i - buffer.length;
|
|
3218
3587
|
styleRule.__parentRule = parentRule;
|
|
@@ -3448,6 +3817,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3448
3817
|
|| parentRule.constructor.name === "CSSMediaRule"
|
|
3449
3818
|
|| parentRule.constructor.name === "CSSSupportsRule"
|
|
3450
3819
|
|| parentRule.constructor.name === "CSSContainerRule"
|
|
3820
|
+
|| parentRule.constructor.name === "CSSScopeRule"
|
|
3451
3821
|
|| parentRule.constructor.name === "CSSLayerBlockRule"
|
|
3452
3822
|
|| parentRule.constructor.name === "CSSStartingStyleRule"
|
|
3453
3823
|
) {
|
|
@@ -3517,6 +3887,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3517
3887
|
|| parentRule.constructor.name === "CSSMediaRule"
|
|
3518
3888
|
|| parentRule.constructor.name === "CSSSupportsRule"
|
|
3519
3889
|
|| parentRule.constructor.name === "CSSContainerRule"
|
|
3890
|
+
|| parentRule.constructor.name === "CSSScopeRule"
|
|
3520
3891
|
|| parentRule.constructor.name === "CSSLayerBlockRule"
|
|
3521
3892
|
|| parentRule.constructor.name === "CSSStartingStyleRule"
|
|
3522
3893
|
) {
|