single-file-core 1.6.8 → 1.6.10
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/core/index.js +83 -7
- package/core/lib/processor-helper-common.js +1 -1
- package/core/lib/processor-helper-inline.js +36 -21
- package/core/lib/processor-helper.js +64 -15
- package/modules/css-identifier.js +55 -0
- package/modules/css-rules-minifier.js +710 -262
- package/modules/css-selector-sanitizer.js +28 -24
- package/modules/css-specificity.js +18 -73
- package/modules/template-formatter.js +1 -0
- package/package.json +1 -1
- package/vendor/css-tree.js +16 -12
|
@@ -25,14 +25,17 @@ import * as cssTree from "./../vendor/css-tree.js";
|
|
|
25
25
|
import { computeMaxSpecificity } from "./css-specificity.js";
|
|
26
26
|
import { parsePrelude } from "./css-scope-prelude-parser.js";
|
|
27
27
|
import { sanitizeSelector, matchUnqueryableAttributeSelector, matchUnqueryablePseudoClass } from "./css-selector-sanitizer.js";
|
|
28
|
+
import { ESCAPE_CHARACTER, decodeIdentifier, decodeName } from "./css-identifier.js";
|
|
28
29
|
|
|
29
30
|
const DEBUG = false;
|
|
30
31
|
|
|
31
32
|
const PSEUDO_ELEMENT_SYNONYMS = new Set(["after", "before", "first-letter", "first-line"]);
|
|
32
|
-
const FUNCTIONAL_PSEUDO_CLASS_NAMES = new Set(["not", "is", "where", "has"]);
|
|
33
|
+
const FUNCTIONAL_PSEUDO_CLASS_NAMES = new Set(["not", "is", "where", "has", "nth-child", "nth-last-child"]);
|
|
33
34
|
const MEDIA_AT_RULE_NAME = "media";
|
|
34
35
|
const SUPPORTS_AT_RULE_NAME = "supports";
|
|
35
|
-
const
|
|
36
|
+
const STARTING_STYLE_AT_RULE_NAME = "starting-style";
|
|
37
|
+
const CONDITIONAL_AT_RULE_NAMES = new Set([MEDIA_AT_RULE_NAME, SUPPORTS_AT_RULE_NAME, "container", STARTING_STYLE_AT_RULE_NAME]);
|
|
38
|
+
const UNCERTAIN_CONDITIONAL_AT_RULE_NAMES = new Set([MEDIA_AT_RULE_NAME, SUPPORTS_AT_RULE_NAME, STARTING_STYLE_AT_RULE_NAME]);
|
|
36
39
|
const RULE_TYPE = "Rule";
|
|
37
40
|
const AT_RULE_TYPE = "Atrule";
|
|
38
41
|
const NESTING_SELECTOR_TYPE = "NestingSelector";
|
|
@@ -41,6 +44,7 @@ const ATTRIBUTE_SELECTOR_TYPE = "AttributeSelector";
|
|
|
41
44
|
const DECLARATION_TYPE = "Declaration";
|
|
42
45
|
const RAW_TYPE = "Raw";
|
|
43
46
|
const VALUE_TYPE = "Value";
|
|
47
|
+
const IDENTIFIER_TYPE = "Identifier";
|
|
44
48
|
const PSEUDO_ELEMENT_SELECTOR_TYPE = "PseudoElementSelector";
|
|
45
49
|
const LAYER_NAME = "layer";
|
|
46
50
|
const SCOPE_NAME = "scope";
|
|
@@ -48,34 +52,61 @@ const IMPORT_NAME = "import";
|
|
|
48
52
|
const FONT_FACE_NAME = "font-face";
|
|
49
53
|
const KEYFRAMES_NAME = "keyframes";
|
|
50
54
|
const COMBINATOR_NAME = "Combinator";
|
|
55
|
+
const TYPE_SELECTOR_TYPE = "TypeSelector";
|
|
51
56
|
const STYLE_ATTRIBUTE_NAME = "style";
|
|
52
57
|
const SELECTOR_LIST_CONTEXT = "selectorList";
|
|
58
|
+
const SCOPE_PRELUDE_OPTIONS = { context: "atrulePrelude", atrule: SCOPE_NAME };
|
|
53
59
|
const STYLESHEET_CONTEXT = "stylesheet";
|
|
54
60
|
const SELECTOR_CONTEXT = "selector";
|
|
55
61
|
const DECLARATION_LIST_CONTEXT = "declarationList";
|
|
56
62
|
const UNKNOWN_PROPERTY_ERROR_NAME = "SyntaxReferenceError";
|
|
63
|
+
const VALUE_MISMATCH_ERROR_NAME = "SyntaxMatchError";
|
|
64
|
+
const VAR_FUNCTION_NAME = "var";
|
|
65
|
+
const FUNCTION_TYPE = "Function";
|
|
66
|
+
const VALIDITY_VALID = "valid";
|
|
67
|
+
const VALIDITY_UNKNOWN = "unknown";
|
|
68
|
+
const VALIDITY_INVALID = "invalid";
|
|
57
69
|
const PARSE_CSS_ERROR_MESSAGE = "Failed to parse CSS";
|
|
58
70
|
const QSA_ERROR_MESSAGE = "Failed to match selector";
|
|
59
71
|
const PRELUDE_SEPARATOR = ",";
|
|
60
|
-
const
|
|
72
|
+
const DECLARATION_SEPARATOR = ":";
|
|
73
|
+
const IS_PSEUDO_CLASS_PREFIX = ":is(";
|
|
74
|
+
const IS_PSEUDO_CLASS_SUFFIX = ")";
|
|
75
|
+
const NO_ELEMENT_SELECTOR = ":not(*)";
|
|
61
76
|
const SCOPE_PSEUDO_CLASS = ":scope";
|
|
77
|
+
const SCOPE_PSEUDO_CLASS_NAME = "scope";
|
|
78
|
+
const DESCENDANT_COMBINATOR = " ";
|
|
79
|
+
const NAMESPACE_SEPARATOR = "|";
|
|
80
|
+
const SELECTOR_SUPPORTS_PREFIX = "selector(";
|
|
81
|
+
const SELECTOR_SUPPORTS_SUFFIX = ")";
|
|
62
82
|
const VENDOR_PREFIX = "-";
|
|
63
83
|
const CUSTOM_PROPERTY_PREFIX = "--";
|
|
64
84
|
const LAYER_NAME_SEPARATOR = ".";
|
|
65
|
-
const
|
|
85
|
+
const LAYER_NAME_KEY_SEPARATOR = "\u0000";
|
|
66
86
|
const CONTEXT_KEY_SEPARATOR = "|";
|
|
87
|
+
const REVERT_LAYER_KEYWORD = "revert-layer";
|
|
88
|
+
const REVERT_LAYER_TEST = /(^|[^-\w])revert-layer([^-\w]|$)/i;
|
|
89
|
+
const NAMESPACE_AT_RULE_NAME = "namespace";
|
|
90
|
+
const URL_TYPE = "Url";
|
|
91
|
+
const STRING_TYPE = "String";
|
|
92
|
+
const UNSCOPED_PROXIMITY = Infinity;
|
|
67
93
|
const BLOCK_OPEN = "{";
|
|
68
94
|
const BLOCK_CLOSE = "}";
|
|
69
95
|
const EMPTY_STRING = "";
|
|
70
96
|
const CSS_IMPORTANCE_NOT_IMPORTANT = 0;
|
|
71
97
|
const CSS_IMPORTANCE_IMPORTANT = 1;
|
|
72
98
|
const INVALID_CSS_ESCAPE_TEST = /\\(?![0-9a-fA-F]{1,6}\s|[^0-9a-zA-Z])/;
|
|
73
|
-
const
|
|
99
|
+
const ANONYMOUS_LAYER_PREFIX = "\u0000";
|
|
100
|
+
const UNCERTAIN_LAYER_ORDER = null;
|
|
101
|
+
const UNDECLARED_LAYER_POSITION = Infinity;
|
|
74
102
|
|
|
75
103
|
export {
|
|
76
104
|
process,
|
|
77
105
|
isUnsupportedPropertyValue,
|
|
78
|
-
|
|
106
|
+
getValueValidity,
|
|
107
|
+
VALIDITY_VALID,
|
|
108
|
+
VALIDITY_UNKNOWN,
|
|
109
|
+
VALIDITY_INVALID
|
|
79
110
|
};
|
|
80
111
|
|
|
81
112
|
function isUnsupportedPropertyValue(property, value) {
|
|
@@ -83,28 +114,41 @@ function isUnsupportedPropertyValue(property, value) {
|
|
|
83
114
|
return Boolean(!match.matched && match.error && match.error.name !== UNKNOWN_PROPERTY_ERROR_NAME);
|
|
84
115
|
}
|
|
85
116
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
// the saved CSS. Four of the five sites in a fifteen-site sweep that use line-clamp were affected.
|
|
92
|
-
function isUnsupportedVendorValue(property, name) {
|
|
93
|
-
if (!name.startsWith(VENDOR_PREFIX)) {
|
|
94
|
-
return false;
|
|
117
|
+
function getValueValidity(property, value) {
|
|
118
|
+
const singleNode = value.children.size === 1 ? value.children.head.data : null;
|
|
119
|
+
const name = singleNode && typeof singleNode.name === "string" ? singleNode.name : null;
|
|
120
|
+
if (name && INVALID_CSS_ESCAPE_TEST.test(name)) {
|
|
121
|
+
return VALIDITY_INVALID;
|
|
95
122
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
123
|
+
const isVendorValue = Boolean(name && name.startsWith(VENDOR_PREFIX));
|
|
124
|
+
if (globalThis.CSS && globalThis.CSS.supports) {
|
|
125
|
+
let supported;
|
|
126
|
+
try {
|
|
127
|
+
supported = globalThis.CSS.supports(property, cssTree.generate(value));
|
|
128
|
+
} catch {
|
|
129
|
+
return VALIDITY_UNKNOWN;
|
|
130
|
+
}
|
|
131
|
+
if (supported) {
|
|
132
|
+
return VALIDITY_VALID;
|
|
133
|
+
}
|
|
134
|
+
return isVendorValue ? VALIDITY_INVALID : VALIDITY_UNKNOWN;
|
|
100
135
|
}
|
|
136
|
+
if (cssTree.find(value, node => node.type === FUNCTION_TYPE && decodeName(node.name) === VAR_FUNCTION_NAME)) {
|
|
137
|
+
return VALIDITY_VALID;
|
|
138
|
+
}
|
|
139
|
+
let match;
|
|
101
140
|
try {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
141
|
+
match = cssTree.lexer.matchProperty(property, value);
|
|
142
|
+
} catch {
|
|
143
|
+
return VALIDITY_UNKNOWN;
|
|
144
|
+
}
|
|
145
|
+
if (match.matched) {
|
|
146
|
+
return VALIDITY_VALID;
|
|
147
|
+
}
|
|
148
|
+
if (match.error && match.error.name === VALUE_MISMATCH_ERROR_NAME && !isVendorValue && !property.startsWith(VENDOR_PREFIX)) {
|
|
149
|
+
return VALIDITY_INVALID;
|
|
107
150
|
}
|
|
151
|
+
return VALIDITY_UNKNOWN;
|
|
108
152
|
}
|
|
109
153
|
|
|
110
154
|
function process(doc, stylesheets) {
|
|
@@ -112,19 +156,30 @@ function process(doc, stylesheets) {
|
|
|
112
156
|
doc,
|
|
113
157
|
stats: { processed: 0, discarded: 0 },
|
|
114
158
|
matchedElements: new Set(),
|
|
159
|
+
revertLayerElements: new Set(),
|
|
160
|
+
revertLayerRules: new WeakMap(),
|
|
161
|
+
revertLayerDeclarations: new WeakMap(),
|
|
162
|
+
hasIndeterminateRevertLayer: false,
|
|
115
163
|
matchedSelectors: new Map(),
|
|
116
164
|
matchingSelectors: new Map(),
|
|
165
|
+
scopeProximities: new Map(),
|
|
166
|
+
scopeProximityKeys: new Map(),
|
|
117
167
|
layerDeclarationCounter: 0,
|
|
118
|
-
|
|
168
|
+
anonymousLayerCounter: 0,
|
|
119
169
|
layerOrder: new Map(),
|
|
170
|
+
layerComparisons: new Map(),
|
|
120
171
|
selectorData: new Map(),
|
|
121
172
|
selectorTexts: new Map(),
|
|
173
|
+
nestingParentTexts: new Map(),
|
|
174
|
+
nestingParentNodes: new Map(),
|
|
175
|
+
scopedSelectorTexts: new Map(),
|
|
176
|
+
supportedSelectors: new Map(),
|
|
177
|
+
valueValidities: new Map(),
|
|
122
178
|
preludeTexts: new Map(),
|
|
123
179
|
rulesCounter: 0,
|
|
124
180
|
scopeIdCounter: 0
|
|
125
181
|
};
|
|
126
182
|
collectLayerOrder(stylesheets, docContext);
|
|
127
|
-
buildEffectiveLayerOrder(docContext);
|
|
128
183
|
minifyRules(stylesheets, docContext);
|
|
129
184
|
computeCascade(docContext);
|
|
130
185
|
removeEmptyRules(stylesheets, docContext);
|
|
@@ -135,36 +190,27 @@ function collectLayerOrder(stylesheets, docContext) {
|
|
|
135
190
|
stylesheets.forEach((stylesheetInfo, key) => {
|
|
136
191
|
if (!stylesheetInfo.scoped && stylesheetInfo.stylesheet && !key.urlNode) {
|
|
137
192
|
if (hasChildNodes(stylesheetInfo.stylesheet)) {
|
|
138
|
-
collectStylesheetLayerOrder(stylesheetInfo.stylesheet.children, { layerStack: [], conditionalStack:
|
|
193
|
+
collectStylesheetLayerOrder(stylesheetInfo.stylesheet.children, { layerStack: [], conditionalStack: getTopConditionalStack(stylesheetInfo) }, docContext);
|
|
139
194
|
}
|
|
140
195
|
}
|
|
141
196
|
});
|
|
142
197
|
}
|
|
143
198
|
|
|
144
|
-
function
|
|
145
|
-
|
|
146
|
-
for (let indexDeclaration = 0; indexDeclaration < docContext.layerDeclarations.length; indexDeclaration++) {
|
|
147
|
-
const declaration = docContext.layerDeclarations[indexDeclaration];
|
|
148
|
-
layerNames.push(declaration.name);
|
|
149
|
-
}
|
|
150
|
-
for (let indexLayerName = 0; indexLayerName < layerNames.length; indexLayerName++) {
|
|
151
|
-
const name = layerNames[indexLayerName];
|
|
152
|
-
if (!docContext.layerOrder.has(name)) {
|
|
153
|
-
docContext.layerOrder.set(name, docContext.layerOrder.size);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
199
|
+
function getTopConditionalStack(stylesheetInfo) {
|
|
200
|
+
return stylesheetInfo.mediaText ? [{ name: MEDIA_AT_RULE_NAME, prelude: stylesheetInfo.mediaText }] : [];
|
|
156
201
|
}
|
|
157
202
|
|
|
158
203
|
function minifyRules(stylesheets, docContext) {
|
|
159
204
|
stylesheets.forEach((stylesheetInfo, key) => {
|
|
160
205
|
if (!stylesheetInfo.scoped && stylesheetInfo.stylesheet && !key.urlNode) {
|
|
161
206
|
if (hasChildNodes(stylesheetInfo.stylesheet)) {
|
|
162
|
-
const topConditionalStack = stylesheetInfo.mediaText ? [{ name: MEDIA_AT_RULE_NAME, prelude: stylesheetInfo.mediaText }] : [];
|
|
163
207
|
minifyStylesheetRules(stylesheetInfo.stylesheet.children, stylesheets, {
|
|
164
208
|
ancestorsSelectors: [],
|
|
165
209
|
layerStack: [],
|
|
166
210
|
scopeStack: [],
|
|
167
|
-
conditionalStack:
|
|
211
|
+
conditionalStack: getTopConditionalStack(stylesheetInfo),
|
|
212
|
+
ownerElement: getStylesheetOwnerElement(key),
|
|
213
|
+
hasDefaultNamespace: hasDefaultNamespace(stylesheetInfo.stylesheet.children)
|
|
168
214
|
}, docContext);
|
|
169
215
|
}
|
|
170
216
|
}
|
|
@@ -174,7 +220,9 @@ function minifyRules(stylesheets, docContext) {
|
|
|
174
220
|
function computeCascade(docContext) {
|
|
175
221
|
const winningDeclarations = new Set();
|
|
176
222
|
docContext.matchedElements.forEach(element => computeCascadedStylesForElement(element, winningDeclarations, docContext));
|
|
177
|
-
|
|
223
|
+
if (!docContext.hasIndeterminateRevertLayer) {
|
|
224
|
+
removeLosingDeclarations(winningDeclarations, docContext);
|
|
225
|
+
}
|
|
178
226
|
}
|
|
179
227
|
|
|
180
228
|
function removeEmptyRules(stylesheets, docContext) {
|
|
@@ -191,8 +239,10 @@ function collectStylesheetLayerOrder(cssRules, layerContext, docContext) {
|
|
|
191
239
|
const { layerStack, conditionalStack } = layerContext;
|
|
192
240
|
for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
|
|
193
241
|
const ruleData = cssRule.data;
|
|
194
|
-
if (ruleData.type === AT_RULE_TYPE && ruleData.name === LAYER_NAME) {
|
|
242
|
+
if (ruleData.type === AT_RULE_TYPE && decodeName(ruleData.name) === LAYER_NAME) {
|
|
195
243
|
collectStylesheetLayerRule(ruleData, layerStack, conditionalStack, docContext);
|
|
244
|
+
} else if (isImportRule(ruleData)) {
|
|
245
|
+
collectImportLayerOrder(ruleData, layerContext, docContext);
|
|
196
246
|
} else if (ruleData.type === AT_RULE_TYPE && hasChildNodes(ruleData.block)) {
|
|
197
247
|
const newConditionalStack = buildConditionalStack(conditionalStack, ruleData, docContext);
|
|
198
248
|
collectStylesheetLayerOrder(ruleData.block.children, { layerStack, conditionalStack: newConditionalStack }, docContext);
|
|
@@ -204,48 +254,136 @@ function collectStylesheetLayerOrder(cssRules, layerContext, docContext) {
|
|
|
204
254
|
|
|
205
255
|
function collectStylesheetLayerRule(ruleData, layerStack, conditionalStack, docContext) {
|
|
206
256
|
if (ruleData.block) {
|
|
207
|
-
const
|
|
208
|
-
registerLayerDeclaration(layerStack,
|
|
209
|
-
collectStylesheetLayerOrder(ruleData.block.children, { layerStack: [...layerStack,
|
|
257
|
+
const layerSegments = getLayerSegments(ruleData, docContext);
|
|
258
|
+
registerLayerDeclaration(layerStack, layerSegments, conditionalStack, docContext);
|
|
259
|
+
collectStylesheetLayerOrder(ruleData.block.children, { layerStack: [...layerStack, ...layerSegments], conditionalStack }, docContext);
|
|
210
260
|
} else if (ruleData.prelude) {
|
|
211
|
-
|
|
212
|
-
layerNames.forEach(layerName => registerLayerDeclaration(layerStack, layerName, conditionalStack, docContext));
|
|
261
|
+
getPreludeText(ruleData.prelude, docContext).split(PRELUDE_SEPARATOR).forEach(layerName => registerLayerDeclaration(layerStack, splitLayerName(layerName), conditionalStack, docContext));
|
|
213
262
|
}
|
|
214
263
|
}
|
|
215
264
|
|
|
265
|
+
function collectImportLayerOrder(ruleData, layerContext, docContext) {
|
|
266
|
+
const urlNode = ruleData.prelude.children.head.data;
|
|
267
|
+
const conditionalStack = buildImportConditionalStack(layerContext.conditionalStack, urlNode);
|
|
268
|
+
const layerSegments = getImportLayerSegments(ruleData, urlNode, docContext);
|
|
269
|
+
let { layerStack } = layerContext;
|
|
270
|
+
if (layerSegments) {
|
|
271
|
+
registerLayerDeclaration(layerStack, layerSegments, conditionalStack, docContext);
|
|
272
|
+
layerStack = [...layerStack, ...layerSegments];
|
|
273
|
+
}
|
|
274
|
+
collectStylesheetLayerOrder(urlNode.importedChildren, { layerStack, conditionalStack }, docContext);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function getLayerSegments(ruleData, docContext) {
|
|
278
|
+
if (!ruleData.layerSegments) {
|
|
279
|
+
ruleData.layerSegments = ruleData.prelude ? splitLayerName(getPreludeText(ruleData.prelude, docContext)) : [createAnonymousLayerSegment(docContext)];
|
|
280
|
+
}
|
|
281
|
+
return ruleData.layerSegments;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function splitLayerName(layerName) {
|
|
285
|
+
const name = layerName.trim();
|
|
286
|
+
const segments = [];
|
|
287
|
+
let segment = EMPTY_STRING;
|
|
288
|
+
for (let index = 0; index < name.length; index++) {
|
|
289
|
+
const character = name.charAt(index);
|
|
290
|
+
if (character === ESCAPE_CHARACTER && index + 1 < name.length) {
|
|
291
|
+
segment += character + name.charAt(index + 1);
|
|
292
|
+
index++;
|
|
293
|
+
} else if (character === LAYER_NAME_SEPARATOR) {
|
|
294
|
+
segments.push(decodeIdentifier(segment));
|
|
295
|
+
segment = EMPTY_STRING;
|
|
296
|
+
} else {
|
|
297
|
+
segment += character;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
segments.push(decodeIdentifier(segment));
|
|
301
|
+
return segments;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function createAnonymousLayerSegment(docContext) {
|
|
305
|
+
return ANONYMOUS_LAYER_PREFIX + docContext.anonymousLayerCounter++;
|
|
306
|
+
}
|
|
307
|
+
|
|
216
308
|
function buildConditionalStack(conditionalStack, ruleData, docContext) {
|
|
217
|
-
const isConditional = CONDITIONAL_AT_RULE_NAMES.has(ruleData.name);
|
|
309
|
+
const isConditional = CONDITIONAL_AT_RULE_NAMES.has(decodeName(ruleData.name));
|
|
218
310
|
return isConditional
|
|
219
311
|
? [...conditionalStack, { name: ruleData.name, prelude: getPreludeText(ruleData.prelude, docContext) }]
|
|
220
312
|
: conditionalStack;
|
|
221
313
|
}
|
|
222
314
|
|
|
223
|
-
function
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
name:
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
315
|
+
function buildImportConditionalStack(conditionalStack, urlNode) {
|
|
316
|
+
const importConditionalStack = [...conditionalStack];
|
|
317
|
+
if (urlNode.importedMediaText) {
|
|
318
|
+
importConditionalStack.push({ name: MEDIA_AT_RULE_NAME, prelude: urlNode.importedMediaText });
|
|
319
|
+
}
|
|
320
|
+
if (urlNode.importedSupportsCondition !== undefined) {
|
|
321
|
+
importConditionalStack.push({ name: SUPPORTS_AT_RULE_NAME, prelude: urlNode.importedSupportsCondition });
|
|
322
|
+
}
|
|
323
|
+
return importConditionalStack;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function getImportLayerSegments(ruleData, urlNode, docContext) {
|
|
327
|
+
if (urlNode.importedLayerName !== undefined) {
|
|
328
|
+
return splitLayerName(urlNode.importedLayerName);
|
|
329
|
+
}
|
|
330
|
+
if (!urlNode.anonymousLayerSegments) {
|
|
331
|
+
const layerKeyword = cssTree.find(ruleData.prelude, node => node.type === IDENTIFIER_TYPE && decodeName(node.name) === LAYER_NAME);
|
|
332
|
+
urlNode.anonymousLayerSegments = layerKeyword ? [createAnonymousLayerSegment(docContext)] : null;
|
|
333
|
+
}
|
|
334
|
+
return urlNode.anonymousLayerSegments;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function isImportRule(ruleData) {
|
|
338
|
+
return ruleData.type === AT_RULE_TYPE && decodeName(ruleData.name) === IMPORT_NAME && hasChildNodes(ruleData.prelude) && Boolean(ruleData.prelude.children.head.data.importedChildren);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function registerLayerDeclaration(layerStack, layerSegments, conditionalStack, docContext) {
|
|
342
|
+
const position = docContext.layerDeclarationCounter++;
|
|
343
|
+
const certain = !conditionalStack.some(context => UNCERTAIN_CONDITIONAL_AT_RULE_NAMES.has(decodeName(context.name)));
|
|
344
|
+
const segments = [...layerStack, ...layerSegments];
|
|
345
|
+
for (let length = layerStack.length + 1; length <= segments.length; length++) {
|
|
346
|
+
const fullLayerName = getFullLayerName(segments.slice(0, length));
|
|
347
|
+
const layerPosition = docContext.layerOrder.get(fullLayerName);
|
|
348
|
+
if (!layerPosition) {
|
|
349
|
+
docContext.layerOrder.set(fullLayerName, { first: position, firstCertain: certain ? position : UNDECLARED_LAYER_POSITION });
|
|
350
|
+
} else if (certain && layerPosition.firstCertain === UNDECLARED_LAYER_POSITION) {
|
|
351
|
+
layerPosition.firstCertain = position;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
230
354
|
}
|
|
231
355
|
|
|
232
356
|
function minifyStylesheetRules(cssRules, stylesheets, processingContext, docContext) {
|
|
233
357
|
const removedRules = new Set();
|
|
358
|
+
let nestedRuleSeen = false;
|
|
359
|
+
let declarationsOrder;
|
|
234
360
|
for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
|
|
235
361
|
docContext.stats.processed++;
|
|
236
|
-
|
|
362
|
+
if (cssRule.data.type === DECLARATION_TYPE) {
|
|
363
|
+
if (nestedRuleSeen) {
|
|
364
|
+
if (declarationsOrder === undefined) {
|
|
365
|
+
declarationsOrder = docContext.rulesCounter++;
|
|
366
|
+
}
|
|
367
|
+
cssRule.data.order = declarationsOrder;
|
|
368
|
+
}
|
|
369
|
+
} else {
|
|
370
|
+
minifyRule(cssRule.data, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
371
|
+
nestedRuleSeen = true;
|
|
372
|
+
declarationsOrder = undefined;
|
|
373
|
+
}
|
|
237
374
|
}
|
|
238
375
|
removedRules.forEach(cssRule => cssRules.remove(cssRule));
|
|
239
376
|
}
|
|
240
377
|
|
|
241
378
|
function minifyRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
|
|
242
|
-
|
|
379
|
+
const atRuleName = decodeName(ruleData.name);
|
|
380
|
+
if (isImportRule(ruleData)) {
|
|
243
381
|
minifyImportRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
244
|
-
} else if (ruleData.type === AT_RULE_TYPE &&
|
|
382
|
+
} else if (ruleData.type === AT_RULE_TYPE && atRuleName === LAYER_NAME && hasChildNodes(ruleData.block)) {
|
|
245
383
|
minifyLayerRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
246
|
-
} else if (ruleData.type === AT_RULE_TYPE &&
|
|
384
|
+
} else if (ruleData.type === AT_RULE_TYPE && atRuleName === SCOPE_NAME && hasChildNodes(ruleData.block)) {
|
|
247
385
|
minifyScopeRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
248
|
-
} else if (ruleData.type === AT_RULE_TYPE &&
|
|
386
|
+
} else if (ruleData.type === AT_RULE_TYPE && atRuleName !== FONT_FACE_NAME && atRuleName !== KEYFRAMES_NAME && !atRuleName.startsWith(VENDOR_PREFIX) && hasChildNodes(ruleData.block)) {
|
|
249
387
|
minifyAtRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
250
388
|
} else if (ruleData.type === RULE_TYPE && hasChildNodes(ruleData.prelude)) {
|
|
251
389
|
minifyStylesheetRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
@@ -254,34 +392,52 @@ function minifyRule(ruleData, cssRule, stylesheets, processingContext, removedRu
|
|
|
254
392
|
|
|
255
393
|
function minifyImportRule(ruleData, _cssRule, stylesheets, processingContext, _removedRules, docContext) {
|
|
256
394
|
const urlNode = ruleData.prelude.children.head.data;
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
if (urlNode.importedSupportsCondition !== undefined) {
|
|
262
|
-
topConditionalStack.push({ name: SUPPORTS_AT_RULE_NAME, prelude: urlNode.importedSupportsCondition });
|
|
263
|
-
}
|
|
395
|
+
const conditionalStack = buildImportConditionalStack(processingContext.conditionalStack, urlNode);
|
|
396
|
+
const layerSegments = getImportLayerSegments(ruleData, urlNode, docContext);
|
|
397
|
+
const layerStack = layerSegments ? [...processingContext.layerStack, ...layerSegments] : processingContext.layerStack;
|
|
264
398
|
minifyStylesheetRules(urlNode.importedChildren, stylesheets, {
|
|
265
399
|
...processingContext,
|
|
266
|
-
|
|
400
|
+
layerStack,
|
|
401
|
+
conditionalStack,
|
|
402
|
+
hasDefaultNamespace: processingContext.hasDefaultNamespace || hasDefaultNamespace(urlNode.importedChildren)
|
|
267
403
|
}, docContext);
|
|
268
404
|
}
|
|
269
405
|
|
|
270
406
|
function minifyLayerRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
|
|
271
|
-
const
|
|
272
|
-
const
|
|
407
|
+
const layerSegments = getLayerSegments(ruleData, docContext);
|
|
408
|
+
const layerStack = [...processingContext.layerStack, ...layerSegments];
|
|
409
|
+
if (!docContext.layerOrder.has(getFullLayerName(layerStack))) {
|
|
410
|
+
registerLayerDeclaration(processingContext.layerStack, layerSegments, processingContext.conditionalStack, docContext);
|
|
411
|
+
}
|
|
412
|
+
const newProcessingContext = { ...processingContext, layerStack };
|
|
273
413
|
expandRawCssRules(ruleData);
|
|
274
414
|
minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
|
|
275
415
|
if (!hasChildNodes(ruleData.block)) {
|
|
416
|
+
removeEmptyLayerRule(ruleData, cssRule, removedRules, docContext);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function removeEmptyLayerRule(ruleData, cssRule, removedRules, docContext) {
|
|
421
|
+
if (hasChildNodes(ruleData.prelude)) {
|
|
422
|
+
ruleData.block = null;
|
|
423
|
+
} else {
|
|
276
424
|
docContext.stats.discarded++;
|
|
277
425
|
removedRules.add(cssRule);
|
|
278
426
|
}
|
|
279
427
|
}
|
|
280
428
|
|
|
429
|
+
function getScopePrelude(ruleData) {
|
|
430
|
+
const { prelude } = ruleData;
|
|
431
|
+
if (prelude && prelude.type === RAW_TYPE) {
|
|
432
|
+
return cssTree.parse(cssTree.generate(prelude), SCOPE_PRELUDE_OPTIONS);
|
|
433
|
+
}
|
|
434
|
+
return prelude;
|
|
435
|
+
}
|
|
436
|
+
|
|
281
437
|
function minifyScopeRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
|
|
282
438
|
let scopeContext;
|
|
283
439
|
try {
|
|
284
|
-
const parsedPrelude = parsePrelude(ruleData
|
|
440
|
+
const parsedPrelude = parsePrelude(getScopePrelude(ruleData));
|
|
285
441
|
scopeContext = buildScopeContext(parsedPrelude, processingContext, docContext);
|
|
286
442
|
} catch (error) {
|
|
287
443
|
if (DEBUG) {
|
|
@@ -296,7 +452,9 @@ function minifyScopeRule(ruleData, cssRule, stylesheets, processingContext, remo
|
|
|
296
452
|
}
|
|
297
453
|
const newProcessingContext = {
|
|
298
454
|
...processingContext,
|
|
299
|
-
scopeStack: [...(processingContext.scopeStack || []), scopeContext]
|
|
455
|
+
scopeStack: [...(processingContext.scopeStack || []), scopeContext],
|
|
456
|
+
hasUnqueryableSelector: processingContext.hasUnqueryableSelector || scopeContext.hasUnqueryableSelector,
|
|
457
|
+
hasNestedUnqueryablePseudoClass: processingContext.hasNestedUnqueryablePseudoClass || scopeContext.hasNestedUnqueryablePseudoClass
|
|
300
458
|
};
|
|
301
459
|
expandRawCssRules(ruleData);
|
|
302
460
|
minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
|
|
@@ -309,50 +467,102 @@ function minifyScopeRule(ruleData, cssRule, stylesheets, processingContext, remo
|
|
|
309
467
|
function buildScopeContext(parsedPrelude, processingContext, docContext) {
|
|
310
468
|
const scopeStack = processingContext.scopeStack || [];
|
|
311
469
|
const includeSelectors = parsedPrelude && parsedPrelude.include ? parsedPrelude.include : [];
|
|
470
|
+
const excludeSelectors = parsedPrelude && parsedPrelude.exclude ? parsedPrelude.exclude : [];
|
|
471
|
+
const includeAnalysis = analyzeScopeSelectors(includeSelectors, processingContext.hasDefaultNamespace);
|
|
472
|
+
const excludeAnalysis = analyzeScopeSelectors(excludeSelectors, processingContext.hasDefaultNamespace);
|
|
312
473
|
let rootElements = [];
|
|
313
474
|
if (includeSelectors.length) {
|
|
314
475
|
rootElements = collectScopeRootElements(includeSelectors, scopeStack, docContext);
|
|
315
|
-
|
|
316
|
-
|
|
476
|
+
if (!rootElements.length && includeAnalysis.hasNestedUnqueryablePseudoClass) {
|
|
477
|
+
rootElements = scopeStack.length ? Array.from(scopeStack[scopeStack.length - 1].rootElements) : getDefaultScopeRoots(docContext);
|
|
478
|
+
}
|
|
317
479
|
} else {
|
|
318
|
-
rootElements =
|
|
480
|
+
rootElements = getImplicitScopeRoots(processingContext.ownerElement, docContext);
|
|
319
481
|
}
|
|
320
482
|
const uniqueRoots = Array.from(new Set(rootElements.filter(Boolean)));
|
|
321
483
|
if (!uniqueRoots.length) {
|
|
322
484
|
return null;
|
|
323
485
|
}
|
|
324
|
-
const boundaryElements = collectScopeBoundaryElements(
|
|
486
|
+
const boundaryElements = collectScopeBoundaryElements(excludeSelectors, uniqueRoots, docContext, processingContext.hasDefaultNamespace);
|
|
325
487
|
return {
|
|
326
488
|
id: docContext.scopeIdCounter++,
|
|
327
489
|
rootElements: new Set(uniqueRoots),
|
|
328
490
|
stopElements: boundaryElements,
|
|
491
|
+
hasUnqueryableSelector: includeAnalysis.hasUnqueryableSelector || excludeAnalysis.hasUnqueryableSelector,
|
|
492
|
+
hasNestedUnqueryablePseudoClass: includeAnalysis.hasNestedUnqueryablePseudoClass
|
|
329
493
|
};
|
|
330
494
|
}
|
|
331
495
|
|
|
496
|
+
function analyzeScopeSelectors(selectors, defaultNamespace) {
|
|
497
|
+
const analysis = { hasUnqueryableSelector: false, hasNestedUnqueryablePseudoClass: false };
|
|
498
|
+
selectors.forEach(selectorInfo => {
|
|
499
|
+
const { hasUnqueryableSelector, hasNestedUnqueryablePseudoClass } = analyzeSelector(selectorInfo.data, defaultNamespace);
|
|
500
|
+
analysis.hasUnqueryableSelector ||= hasUnqueryableSelector;
|
|
501
|
+
analysis.hasNestedUnqueryablePseudoClass ||= hasNestedUnqueryablePseudoClass;
|
|
502
|
+
});
|
|
503
|
+
return analysis;
|
|
504
|
+
}
|
|
505
|
+
|
|
332
506
|
function collectScopeRootElements(includeSelectors, scopeStack, docContext) {
|
|
333
507
|
const roots = new Set();
|
|
334
508
|
includeSelectors.forEach(selectorInfo => {
|
|
335
|
-
const selectorText = sanitizeSelector(selectorInfo,
|
|
336
|
-
const matchedNodes =
|
|
509
|
+
const selectorText = sanitizeSelector(selectorInfo, docContext);
|
|
510
|
+
const matchedNodes = scopeStack.length
|
|
511
|
+
? matchElementsInScope(getScopedSelectorText(selectorText, docContext), scopeStack)
|
|
512
|
+
: querySelectorAll(docContext.doc, selectorText);
|
|
337
513
|
filterElementsByScopes(matchedNodes, scopeStack).forEach(match => roots.add(match));
|
|
338
514
|
});
|
|
339
515
|
return Array.from(roots);
|
|
340
516
|
}
|
|
341
517
|
|
|
342
|
-
function collectScopeBoundaryElements(excludeSelectors, rootElements, docContext) {
|
|
343
|
-
const boundaries = new
|
|
518
|
+
function collectScopeBoundaryElements(excludeSelectors, rootElements, docContext, defaultNamespace) {
|
|
519
|
+
const boundaries = new Map();
|
|
344
520
|
if (!excludeSelectors.length || !rootElements.length) {
|
|
345
521
|
return boundaries;
|
|
346
522
|
}
|
|
347
523
|
excludeSelectors.forEach(selectorInfo => {
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
524
|
+
const { hasUnqueryableSelector, hasNestedUnqueryablePseudoClass } = analyzeSelector(selectorInfo.data, defaultNamespace);
|
|
525
|
+
if (!hasUnqueryableSelector && !hasNestedUnqueryablePseudoClass) {
|
|
526
|
+
const selectorText = getScopedSelectorText(sanitizeSelector(selectorInfo, docContext), docContext);
|
|
527
|
+
rootElements.forEach(root => {
|
|
528
|
+
const rootBoundaries = boundaries.get(root) || new Set();
|
|
529
|
+
matchSelectorWithinRoot(root, selectorText).forEach(node => rootBoundaries.add(node));
|
|
530
|
+
if (rootBoundaries.size) {
|
|
531
|
+
boundaries.set(root, rootBoundaries);
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
}
|
|
352
535
|
});
|
|
353
536
|
return boundaries;
|
|
354
537
|
}
|
|
355
538
|
|
|
539
|
+
function hasDefaultNamespace(cssRules) {
|
|
540
|
+
for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
|
|
541
|
+
const ruleData = cssRule.data;
|
|
542
|
+
if (ruleData.type === AT_RULE_TYPE && ruleData.name && decodeName(ruleData.name) === NAMESPACE_AT_RULE_NAME) {
|
|
543
|
+
const prelude = ruleData.prelude && ruleData.prelude.children && ruleData.prelude.children.head;
|
|
544
|
+
if (prelude && (prelude.data.type === URL_TYPE || prelude.data.type === STRING_TYPE)) {
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
return false;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function getStylesheetOwnerElement(key) {
|
|
553
|
+
if (key && key.element) {
|
|
554
|
+
return key.element;
|
|
555
|
+
}
|
|
556
|
+
return key && key.nodeType === 1 ? key : null;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function getImplicitScopeRoots(ownerElement, docContext) {
|
|
560
|
+
if (ownerElement && ownerElement.parentElement) {
|
|
561
|
+
return [ownerElement.parentElement];
|
|
562
|
+
}
|
|
563
|
+
return getDefaultScopeRoots(docContext);
|
|
564
|
+
}
|
|
565
|
+
|
|
356
566
|
function getDefaultScopeRoots(docContext) {
|
|
357
567
|
const roots = [];
|
|
358
568
|
if (docContext.doc && docContext.doc.documentElement) {
|
|
@@ -380,7 +590,11 @@ function minifyAtRule(ruleData, cssRule, stylesheets, processingContext, removed
|
|
|
380
590
|
|
|
381
591
|
function minifyStylesheetRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
|
|
382
592
|
ruleData.order = docContext.rulesCounter++;
|
|
383
|
-
|
|
593
|
+
if (!isPreludeSupported(ruleData.prelude, docContext)) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
expandRawCssRules(ruleData);
|
|
597
|
+
const removedSelectors = getRemovableSelectors(ruleData, processSelectors(ruleData, processingContext, docContext), docContext);
|
|
384
598
|
const wasDiscarded = removeUnmatchedSelectors(ruleData, removedSelectors, removedRules, cssRule, docContext);
|
|
385
599
|
if (!wasDiscarded && hasChildNodes(ruleData.block)) {
|
|
386
600
|
processNestedRules(ruleData, stylesheets, processingContext, docContext);
|
|
@@ -391,32 +605,70 @@ function processSelectors(ruleData, processingContext, docContext) {
|
|
|
391
605
|
const removedSelectors = [];
|
|
392
606
|
const { ancestorsSelectors, scopeStack } = processingContext;
|
|
393
607
|
for (let selector = ruleData.prelude.children.head, selectorIndex = 0; selector; selector = selector.next, selectorIndex++) {
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
} = analyzeSelector(selector.data);
|
|
608
|
+
const analysis = analyzeSelector(selector.data, processingContext.hasDefaultNamespace);
|
|
609
|
+
const { startsWithCombinator } = analysis;
|
|
610
|
+
const hasUnqueryableSelector = analysis.hasUnqueryableSelector || Boolean(processingContext.hasUnqueryableSelector);
|
|
611
|
+
const hasNestedUnqueryablePseudoClass = analysis.hasNestedUnqueryablePseudoClass || Boolean(processingContext.hasNestedUnqueryablePseudoClass);
|
|
399
612
|
if (hasUnqueryableSelector) {
|
|
400
613
|
ruleData.hasUnqueryableSelector = true;
|
|
401
614
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
615
|
+
if (hasNestedUnqueryablePseudoClass) {
|
|
616
|
+
ruleData.hasNestedUnqueryablePseudoClass = true;
|
|
617
|
+
}
|
|
618
|
+
const nestedSelector = getNestedSelector(selector, ancestorsSelectors, docContext);
|
|
619
|
+
registerSelector(selector, nestedSelector, ruleData, processingContext, docContext);
|
|
620
|
+
const relativeToScope = startsWithCombinator && !(ancestorsSelectors && ancestorsSelectors.length) && Boolean(scopeStack && scopeStack.length);
|
|
621
|
+
const matchedElements = matchElements(selector, nestedSelector, scopeStack, docContext, relativeToScope);
|
|
622
|
+
if (hasNestedUnqueryablePseudoClass) {
|
|
623
|
+
registerIndeterminateRevertLayer(ruleData, docContext);
|
|
624
|
+
}
|
|
625
|
+
if (matchedElements.length) {
|
|
626
|
+
if (hasUnqueryableSelector) {
|
|
627
|
+
registerRevertLayerElements(ruleData, matchedElements, docContext);
|
|
628
|
+
} else {
|
|
629
|
+
updateMatchingSelectors(matchedElements, selector, docContext);
|
|
412
630
|
}
|
|
631
|
+
} else if (!hasNestedUnqueryablePseudoClass) {
|
|
632
|
+
removedSelectors.push(selector);
|
|
413
633
|
}
|
|
414
634
|
}
|
|
415
635
|
return removedSelectors;
|
|
416
636
|
}
|
|
417
637
|
|
|
418
|
-
function
|
|
419
|
-
|
|
638
|
+
function getRemovableSelectors(ruleData, removedSelectors, docContext) {
|
|
639
|
+
if (!removedSelectors.length || removedSelectors.length === ruleData.prelude.children.size || !hasNestedRules(ruleData)) {
|
|
640
|
+
return removedSelectors;
|
|
641
|
+
}
|
|
642
|
+
const removedSet = new Set(removedSelectors);
|
|
643
|
+
let keptSpecificity = { a: 0, b: 0, c: 0 };
|
|
644
|
+
for (let selector = ruleData.prelude.children.head; selector; selector = selector.next) {
|
|
645
|
+
if (!removedSet.has(selector)) {
|
|
646
|
+
const { specificity } = docContext.selectorData.get(selector);
|
|
647
|
+
if (compareSpecificities(specificity, keptSpecificity) > 0) {
|
|
648
|
+
keptSpecificity = specificity;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return removedSelectors.filter(selector => compareSpecificities(docContext.selectorData.get(selector).specificity, keptSpecificity) <= 0);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function hasNestedRules(ruleData) {
|
|
656
|
+
if (hasChildNodes(ruleData.block)) {
|
|
657
|
+
for (let child = ruleData.block.children.head; child; child = child.next) {
|
|
658
|
+
if (child.data.type !== DECLARATION_TYPE) {
|
|
659
|
+
return true;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return false;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function compareSpecificities(specificityA, specificityB) {
|
|
667
|
+
return (specificityA.a - specificityB.a) || (specificityA.b - specificityB.b) || (specificityA.c - specificityB.c);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function analyzeSelector(selector, defaultNamespace) {
|
|
671
|
+
let hasUnqueryableSelector = Boolean(defaultNamespace);
|
|
420
672
|
let hasNestedUnqueryablePseudoClass = false;
|
|
421
673
|
let startsWithCombinator = false;
|
|
422
674
|
let functionalPseudoClassDepth = 0;
|
|
@@ -428,7 +680,7 @@ function analyzeSelector(selector) {
|
|
|
428
680
|
hasNestedUnqueryablePseudoClass = true;
|
|
429
681
|
}
|
|
430
682
|
} else if (node.type === PSEUDO_CLASS_SELECTOR_TYPE) {
|
|
431
|
-
if (PSEUDO_ELEMENT_SYNONYMS.has(node.name) || matchUnqueryablePseudoClass(node)) {
|
|
683
|
+
if (PSEUDO_ELEMENT_SYNONYMS.has(decodeName(node.name)) || matchUnqueryablePseudoClass(node)) {
|
|
432
684
|
hasUnqueryableSelector = true;
|
|
433
685
|
if (functionalPseudoClassDepth) {
|
|
434
686
|
hasNestedUnqueryablePseudoClass = true;
|
|
@@ -442,6 +694,11 @@ function analyzeSelector(selector) {
|
|
|
442
694
|
if (functionalPseudoClassDepth) {
|
|
443
695
|
hasNestedUnqueryablePseudoClass = true;
|
|
444
696
|
}
|
|
697
|
+
} else if (node.type === TYPE_SELECTOR_TYPE && typeof node.name === "string" && node.name.includes(NAMESPACE_SEPARATOR)) {
|
|
698
|
+
hasUnqueryableSelector = true;
|
|
699
|
+
if (functionalPseudoClassDepth) {
|
|
700
|
+
hasNestedUnqueryablePseudoClass = true;
|
|
701
|
+
}
|
|
445
702
|
}
|
|
446
703
|
},
|
|
447
704
|
leave(node) {
|
|
@@ -455,6 +712,45 @@ function analyzeSelector(selector) {
|
|
|
455
712
|
return { hasUnqueryableSelector, hasNestedUnqueryablePseudoClass, startsWithCombinator };
|
|
456
713
|
}
|
|
457
714
|
|
|
715
|
+
function isPreludeSupported(prelude, docContext) {
|
|
716
|
+
if (!globalThis.CSS || !globalThis.CSS.supports) {
|
|
717
|
+
return true;
|
|
718
|
+
}
|
|
719
|
+
for (let selector = prelude.children.head; selector; selector = selector.next) {
|
|
720
|
+
if (!isSelectorSupported(selector.data, docContext)) {
|
|
721
|
+
return false;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return true;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function isSelectorSupported(selector, docContext) {
|
|
728
|
+
const selectorNode = cssTree.clone(selector);
|
|
729
|
+
cssTree.walk(selectorNode, {
|
|
730
|
+
visit: TYPE_SELECTOR_TYPE,
|
|
731
|
+
enter(node) {
|
|
732
|
+
if (typeof node.name === "string" && node.name.includes(NAMESPACE_SEPARATOR)) {
|
|
733
|
+
node.name = node.name.substring(node.name.lastIndexOf(NAMESPACE_SEPARATOR) + 1);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
let selectorText = cssTree.generate(selectorNode);
|
|
738
|
+
const firstChild = selectorNode.children && selectorNode.children.head && selectorNode.children.head.data;
|
|
739
|
+
if (firstChild && firstChild.type === COMBINATOR_NAME) {
|
|
740
|
+
selectorText = SCOPE_PSEUDO_CLASS + selectorText;
|
|
741
|
+
}
|
|
742
|
+
if (!docContext.supportedSelectors.has(selectorText)) {
|
|
743
|
+
let supported;
|
|
744
|
+
try {
|
|
745
|
+
supported = globalThis.CSS.supports(SELECTOR_SUPPORTS_PREFIX + selectorText + SELECTOR_SUPPORTS_SUFFIX);
|
|
746
|
+
} catch {
|
|
747
|
+
supported = true;
|
|
748
|
+
}
|
|
749
|
+
docContext.supportedSelectors.set(selectorText, supported);
|
|
750
|
+
}
|
|
751
|
+
return docContext.supportedSelectors.get(selectorText);
|
|
752
|
+
}
|
|
753
|
+
|
|
458
754
|
function updateMatchingSelectors(matchedElements, selector, docContext) {
|
|
459
755
|
matchedElements.forEach(element => {
|
|
460
756
|
docContext.matchedElements.add(element);
|
|
@@ -469,28 +765,31 @@ function updateMatchingSelectors(matchedElements, selector, docContext) {
|
|
|
469
765
|
}
|
|
470
766
|
|
|
471
767
|
function processNestedRules(ruleData, stylesheets, processingContext, docContext) {
|
|
472
|
-
|
|
473
|
-
|
|
768
|
+
const newProcessingContext = {
|
|
769
|
+
...processingContext,
|
|
770
|
+
ancestorsSelectors: [...processingContext.ancestorsSelectors, ruleData.prelude],
|
|
771
|
+
hasUnqueryableSelector: Boolean(ruleData.hasUnqueryableSelector),
|
|
772
|
+
hasNestedUnqueryablePseudoClass: Boolean(ruleData.hasNestedUnqueryablePseudoClass)
|
|
773
|
+
};
|
|
474
774
|
minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
|
|
475
775
|
}
|
|
476
776
|
|
|
477
|
-
function registerSelector(selector, ruleData, processingContext, docContext) {
|
|
777
|
+
function registerSelector(selector, nestedSelector, ruleData, processingContext, docContext) {
|
|
478
778
|
const {
|
|
479
|
-
ancestorsSelectors,
|
|
480
779
|
layerStack,
|
|
481
780
|
scopeStack,
|
|
482
781
|
conditionalStack
|
|
483
782
|
} = processingContext;
|
|
484
783
|
docContext.selectorData.set(selector, {
|
|
485
|
-
specificity: computeMaxSpecificity(selector.data
|
|
784
|
+
specificity: computeMaxSpecificity(nestedSelector === null ? selector.data : nestedSelector.data),
|
|
486
785
|
rule: ruleData,
|
|
487
786
|
layerStack,
|
|
787
|
+
scopeStack,
|
|
488
788
|
conditionalStack
|
|
489
789
|
});
|
|
490
790
|
}
|
|
491
791
|
|
|
492
792
|
function computeCascadedStylesForElement(element, winningDeclarations, docContext) {
|
|
493
|
-
const cascadedStyles = new Map();
|
|
494
793
|
const allDeclarations = collectDeclarationItemsForElement(element, docContext);
|
|
495
794
|
const contextGroups = new Map();
|
|
496
795
|
allDeclarations.forEach(declarationData => {
|
|
@@ -502,18 +801,109 @@ function computeCascadedStylesForElement(element, winningDeclarations, docContex
|
|
|
502
801
|
}
|
|
503
802
|
contextGroups.get(contextKey).push(declarationData);
|
|
504
803
|
});
|
|
804
|
+
let hasWinningRevertLayer = false;
|
|
505
805
|
contextGroups.forEach(declarations => {
|
|
506
806
|
declarations.sort((declarationA, declarationB) => compareDeclarations(declarationA, declarationB, docContext));
|
|
807
|
+
const propertyDeclarations = new Map();
|
|
507
808
|
declarations.forEach(declarationData => {
|
|
508
|
-
const {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
});
|
|
809
|
+
const { property } = declarationData.declaration.data;
|
|
810
|
+
if (!propertyDeclarations.has(property)) {
|
|
811
|
+
propertyDeclarations.set(property, []);
|
|
812
|
+
}
|
|
813
|
+
propertyDeclarations.get(property).push(declarationData);
|
|
514
814
|
});
|
|
815
|
+
propertyDeclarations.forEach(candidates => {
|
|
816
|
+
if (hasUncertainLayerOrder(candidates, docContext)) {
|
|
817
|
+
candidates.forEach(candidate => winningDeclarations.add(candidate.declaration));
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
for (let indexCandidate = candidates.length - 1; indexCandidate >= 0; indexCandidate--) {
|
|
821
|
+
const { declaration, validity } = candidates[indexCandidate];
|
|
822
|
+
winningDeclarations.add(declaration);
|
|
823
|
+
if (validity === VALIDITY_VALID) {
|
|
824
|
+
hasWinningRevertLayer ||= hasRevertLayerKeyword(declaration, docContext);
|
|
825
|
+
break;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
});
|
|
830
|
+
if (hasWinningRevertLayer || docContext.revertLayerElements.has(element)) {
|
|
831
|
+
allDeclarations.forEach(({ declaration }) => winningDeclarations.add(declaration));
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
function hasUncertainLayerOrder(candidates, docContext) {
|
|
836
|
+
const layerStacks = [new Map(), new Map()];
|
|
837
|
+
candidates.forEach(({ declaration, selector }) => {
|
|
838
|
+
if (selector) {
|
|
839
|
+
const { layerStack } = docContext.selectorData.get(selector);
|
|
840
|
+
layerStacks[declaration.data.important ? CSS_IMPORTANCE_IMPORTANT : CSS_IMPORTANCE_NOT_IMPORTANT].set(getFullLayerName(layerStack), layerStack);
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
return layerStacks.some(importanceStacks => {
|
|
844
|
+
const stacks = Array.from(importanceStacks.values());
|
|
845
|
+
for (let indexA = 0; indexA < stacks.length; indexA++) {
|
|
846
|
+
for (let indexB = indexA + 1; indexB < stacks.length; indexB++) {
|
|
847
|
+
if (compareLayers(stacks[indexA], stacks[indexB], docContext) === UNCERTAIN_LAYER_ORDER) {
|
|
848
|
+
return true;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return false;
|
|
515
853
|
});
|
|
516
|
-
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
function registerRevertLayerElements(ruleData, matchedElements, docContext) {
|
|
857
|
+
if (hasRevertLayerDeclaration(ruleData, docContext)) {
|
|
858
|
+
matchedElements.forEach(element => docContext.revertLayerElements.add(element));
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
function registerIndeterminateRevertLayer(ruleData, docContext) {
|
|
863
|
+
if (hasRevertLayerDeclaration(ruleData, docContext)) {
|
|
864
|
+
docContext.hasIndeterminateRevertLayer = true;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function hasRevertLayerDeclaration(ruleData, docContext) {
|
|
869
|
+
let found = docContext.revertLayerRules.get(ruleData);
|
|
870
|
+
if (found === undefined) {
|
|
871
|
+
found = findRevertLayerDeclaration(ruleData, docContext);
|
|
872
|
+
docContext.revertLayerRules.set(ruleData, found);
|
|
873
|
+
}
|
|
874
|
+
return found;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
function findRevertLayerDeclaration(ruleData, docContext) {
|
|
878
|
+
if (!hasChildNodes(ruleData.block)) {
|
|
879
|
+
return false;
|
|
880
|
+
}
|
|
881
|
+
for (let child = ruleData.block.children.head; child; child = child.next) {
|
|
882
|
+
if (child.data.type === DECLARATION_TYPE && hasRevertLayerKeyword(child, docContext)) {
|
|
883
|
+
return true;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
return false;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
function hasRevertLayerKeyword(declaration, docContext) {
|
|
890
|
+
let found = docContext.revertLayerDeclarations.get(declaration);
|
|
891
|
+
if (found === undefined) {
|
|
892
|
+
found = findRevertLayerKeyword(declaration);
|
|
893
|
+
docContext.revertLayerDeclarations.set(declaration, found);
|
|
894
|
+
}
|
|
895
|
+
return found;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
function findRevertLayerKeyword(declaration) {
|
|
899
|
+
const { value } = declaration.data;
|
|
900
|
+
if (!value) {
|
|
901
|
+
return false;
|
|
902
|
+
}
|
|
903
|
+
if (value.type === RAW_TYPE) {
|
|
904
|
+
return REVERT_LAYER_TEST.test(decodeIdentifier(value.value));
|
|
905
|
+
}
|
|
906
|
+
return Boolean(cssTree.find(value, node => node.type === IDENTIFIER_TYPE && decodeIdentifier(node.name).toLowerCase() === REVERT_LAYER_KEYWORD));
|
|
517
907
|
}
|
|
518
908
|
|
|
519
909
|
function createContextKey(conditionalStack) {
|
|
@@ -524,13 +914,15 @@ function collectDeclarationItemsForElement(element, docContext) {
|
|
|
524
914
|
const matchingSelectors = docContext.matchingSelectors.get(element);
|
|
525
915
|
const allDeclarations = [];
|
|
526
916
|
matchingSelectors.forEach(selector => {
|
|
527
|
-
const
|
|
917
|
+
const selectorData = docContext.selectorData.get(selector);
|
|
918
|
+
const cssRule = selectorData.rule;
|
|
528
919
|
if (hasChildNodes(cssRule.block)) {
|
|
920
|
+
const proximity = getScopeProximity(element, selector, docContext);
|
|
529
921
|
const declarations = cssRule.block.children;
|
|
530
922
|
for (let declaration = declarations.head; declaration; declaration = declaration.next) {
|
|
531
|
-
const { type, value } = declaration.data;
|
|
923
|
+
const { type, value, order } = declaration.data;
|
|
532
924
|
if (type === DECLARATION_TYPE && value) {
|
|
533
|
-
addDeclaration(declaration,
|
|
925
|
+
addDeclaration(declaration, selectorData.specificity, false, selector, order === undefined ? cssRule.order : order, proximity);
|
|
534
926
|
}
|
|
535
927
|
}
|
|
536
928
|
}
|
|
@@ -541,36 +933,41 @@ function collectDeclarationItemsForElement(element, docContext) {
|
|
|
541
933
|
}
|
|
542
934
|
return allDeclarations;
|
|
543
935
|
|
|
544
|
-
function addDeclaration(declaration, specificity, isInline, selector) {
|
|
936
|
+
function addDeclaration(declaration, specificity, isInline, selector, order, proximity) {
|
|
545
937
|
const { property, value } = declaration.data;
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
} catch {
|
|
559
|
-
// ignored
|
|
560
|
-
}
|
|
938
|
+
if (value.type === VALUE_TYPE && hasChildNodes(value)) {
|
|
939
|
+
const validity = getCachedValueValidity(property, value, docContext);
|
|
940
|
+
if (validity !== VALIDITY_INVALID) {
|
|
941
|
+
allDeclarations.push({
|
|
942
|
+
declaration,
|
|
943
|
+
selector,
|
|
944
|
+
specificity,
|
|
945
|
+
isInline,
|
|
946
|
+
order,
|
|
947
|
+
proximity,
|
|
948
|
+
validity
|
|
949
|
+
});
|
|
561
950
|
}
|
|
562
951
|
}
|
|
563
|
-
if (hasValueChildNodes && !isRawValue && !isInvalidValue) {
|
|
564
|
-
allDeclarations.push({
|
|
565
|
-
declaration,
|
|
566
|
-
selector,
|
|
567
|
-
specificity,
|
|
568
|
-
isInline
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
952
|
}
|
|
572
953
|
}
|
|
573
954
|
|
|
955
|
+
function getCachedValueValidity(property, value, docContext) {
|
|
956
|
+
if (property.startsWith(CUSTOM_PROPERTY_PREFIX)) {
|
|
957
|
+
return VALIDITY_VALID;
|
|
958
|
+
}
|
|
959
|
+
if (!docContext.valueValidities.has(value)) {
|
|
960
|
+
docContext.valueValidities.set(value, getValueValidity(property, value));
|
|
961
|
+
}
|
|
962
|
+
return docContext.valueValidities.get(value);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
function getScopeProximity(element, selector, docContext) {
|
|
966
|
+
const proximities = docContext.scopeProximities.get(docContext.scopeProximityKeys.get(selector));
|
|
967
|
+
const proximity = proximities && proximities.get(element);
|
|
968
|
+
return proximity === undefined ? UNSCOPED_PROXIMITY : proximity;
|
|
969
|
+
}
|
|
970
|
+
|
|
574
971
|
function getConditionalStackForSelector(selector, docContext) {
|
|
575
972
|
let conditionalStack = [];
|
|
576
973
|
if (selector) {
|
|
@@ -582,20 +979,27 @@ function getConditionalStackForSelector(selector, docContext) {
|
|
|
582
979
|
return conditionalStack;
|
|
583
980
|
}
|
|
584
981
|
|
|
585
|
-
function matchElements(selector,
|
|
586
|
-
let selectorText =
|
|
982
|
+
function matchElements(selector, nestedSelector, scopeStack, docContext, relativeToScope) {
|
|
983
|
+
let selectorText = sanitizeSelector(nestedSelector === null ? selector : nestedSelector, docContext);
|
|
587
984
|
if (relativeToScope) {
|
|
588
985
|
selectorText = SCOPE_PSEUDO_CLASS + selectorText;
|
|
986
|
+
} else if (scopeStack && scopeStack.length) {
|
|
987
|
+
selectorText = getScopedSelectorText(selectorText, docContext);
|
|
589
988
|
}
|
|
590
989
|
const cacheKey = createScopeCacheKey(selectorText, scopeStack);
|
|
990
|
+
if (scopeStack && scopeStack.length) {
|
|
991
|
+
docContext.scopeProximityKeys.set(selector, cacheKey);
|
|
992
|
+
}
|
|
591
993
|
const cachedNodes = docContext.matchedSelectors.get(cacheKey);
|
|
592
994
|
if (cachedNodes) {
|
|
593
995
|
return cachedNodes;
|
|
594
996
|
}
|
|
595
997
|
let nodes;
|
|
596
998
|
if (scopeStack && scopeStack.length) {
|
|
597
|
-
|
|
999
|
+
const proximities = new Map();
|
|
1000
|
+
nodes = matchElementsInScope(selectorText, scopeStack, proximities);
|
|
598
1001
|
nodes = filterElementsByScopes(nodes, scopeStack);
|
|
1002
|
+
docContext.scopeProximities.set(cacheKey, proximities);
|
|
599
1003
|
} else {
|
|
600
1004
|
nodes = querySelectorAll(docContext.doc, selectorText);
|
|
601
1005
|
}
|
|
@@ -603,6 +1007,26 @@ function matchElements(selector, ancestorsSelectors, scopeStack, docContext, rel
|
|
|
603
1007
|
return nodes;
|
|
604
1008
|
}
|
|
605
1009
|
|
|
1010
|
+
function getScopedSelectorText(selectorText, docContext) {
|
|
1011
|
+
if (!docContext.scopedSelectorTexts.has(selectorText)) {
|
|
1012
|
+
let scopedSelectorText = selectorText;
|
|
1013
|
+
try {
|
|
1014
|
+
const selectorList = parseCss(selectorText, SELECTOR_LIST_CONTEXT);
|
|
1015
|
+
scopedSelectorText = selectorList.children.toArray().map(selector => {
|
|
1016
|
+
const hasScopePseudoClass = cssTree.find(selector, node => node.type === PSEUDO_CLASS_SELECTOR_TYPE && decodeName(node.name) === SCOPE_PSEUDO_CLASS_NAME);
|
|
1017
|
+
return hasScopePseudoClass ? cssTree.generate(selector) : SCOPE_PSEUDO_CLASS + DESCENDANT_COMBINATOR + cssTree.generate(selector);
|
|
1018
|
+
}).join(PRELUDE_SEPARATOR);
|
|
1019
|
+
} catch {
|
|
1020
|
+
if (DEBUG) {
|
|
1021
|
+
// eslint-disable-next-line no-console
|
|
1022
|
+
console.warn(PARSE_CSS_ERROR_MESSAGE, selectorText);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
docContext.scopedSelectorTexts.set(selectorText, scopedSelectorText);
|
|
1026
|
+
}
|
|
1027
|
+
return docContext.scopedSelectorTexts.get(selectorText);
|
|
1028
|
+
}
|
|
1029
|
+
|
|
606
1030
|
function createScopeCacheKey(selectorText, scopeStack) {
|
|
607
1031
|
if (!scopeStack || !scopeStack.length) {
|
|
608
1032
|
return selectorText;
|
|
@@ -611,7 +1035,7 @@ function createScopeCacheKey(selectorText, scopeStack) {
|
|
|
611
1035
|
return `${selectorText}${CONTEXT_KEY_SEPARATOR}${signature}`;
|
|
612
1036
|
}
|
|
613
1037
|
|
|
614
|
-
function matchElementsInScope(selectorText, scopeStack) {
|
|
1038
|
+
function matchElementsInScope(selectorText, scopeStack, proximities) {
|
|
615
1039
|
const currentScope = scopeStack[scopeStack.length - 1];
|
|
616
1040
|
const roots = Array.from(currentScope.rootElements);
|
|
617
1041
|
if (!roots.length) {
|
|
@@ -619,7 +1043,19 @@ function matchElementsInScope(selectorText, scopeStack) {
|
|
|
619
1043
|
}
|
|
620
1044
|
const matchedNodes = new Set();
|
|
621
1045
|
roots.forEach(root => {
|
|
622
|
-
|
|
1046
|
+
const stopElements = currentScope.stopElements && currentScope.stopElements.get(root);
|
|
1047
|
+
matchSelectorWithinRoot(root, selectorText).forEach(node => {
|
|
1048
|
+
const proximity = getRootProximity(node, root, stopElements);
|
|
1049
|
+
if (proximity !== UNSCOPED_PROXIMITY) {
|
|
1050
|
+
matchedNodes.add(node);
|
|
1051
|
+
if (proximities) {
|
|
1052
|
+
const knownProximity = proximities.get(node);
|
|
1053
|
+
if (knownProximity === undefined || proximity < knownProximity) {
|
|
1054
|
+
proximities.set(node, proximity);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
});
|
|
623
1059
|
});
|
|
624
1060
|
return Array.from(matchedNodes);
|
|
625
1061
|
}
|
|
@@ -627,7 +1063,7 @@ function matchElementsInScope(selectorText, scopeStack) {
|
|
|
627
1063
|
function matchSelectorWithinRoot(root, selectorText) {
|
|
628
1064
|
const matchedNodes = new Set();
|
|
629
1065
|
if (!root || root.nodeType !== 1) {
|
|
630
|
-
return
|
|
1066
|
+
return [];
|
|
631
1067
|
}
|
|
632
1068
|
if (matches(root, selectorText)) {
|
|
633
1069
|
matchedNodes.add(root);
|
|
@@ -689,17 +1125,10 @@ function getTraversalRoots(root) {
|
|
|
689
1125
|
return [];
|
|
690
1126
|
}
|
|
691
1127
|
if (root.nodeType === 9 || root.nodeType === 11) {
|
|
692
|
-
const roots = [];
|
|
693
1128
|
if (root.documentElement) {
|
|
694
|
-
|
|
695
|
-
}
|
|
696
|
-
if (root.body && (!roots.length || root.body !== roots[0])) {
|
|
697
|
-
roots.push(root.body);
|
|
1129
|
+
return [root.documentElement];
|
|
698
1130
|
}
|
|
699
|
-
|
|
700
|
-
roots.push(...Array.from(root.children).filter(node => node.nodeType === 1));
|
|
701
|
-
}
|
|
702
|
-
return roots;
|
|
1131
|
+
return root.children ? Array.from(root.children).filter(node => node.nodeType === 1) : [];
|
|
703
1132
|
}
|
|
704
1133
|
return [root];
|
|
705
1134
|
}
|
|
@@ -724,30 +1153,81 @@ function isElementWithinScopes(element, scopeStack) {
|
|
|
724
1153
|
}
|
|
725
1154
|
|
|
726
1155
|
function isElementWithinScope(element, scopeContext) {
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
if (
|
|
730
|
-
return false;
|
|
731
|
-
}
|
|
732
|
-
if (scopeContext.rootElements.has(current)) {
|
|
1156
|
+
const { rootElements, stopElements } = scopeContext;
|
|
1157
|
+
for (let current = element; current && current.nodeType === 1; current = current.parentElement) {
|
|
1158
|
+
if (rootElements.has(current) && getRootProximity(element, current, stopElements && stopElements.get(current)) !== UNSCOPED_PROXIMITY) {
|
|
733
1159
|
return true;
|
|
734
1160
|
}
|
|
735
|
-
current = current.parentElement;
|
|
736
1161
|
}
|
|
737
1162
|
return false;
|
|
738
1163
|
}
|
|
739
1164
|
|
|
740
|
-
function
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
1165
|
+
function getRootProximity(element, root, stopElements) {
|
|
1166
|
+
const ancestors = [];
|
|
1167
|
+
for (let current = element; current && current.nodeType === 1; current = current.parentElement) {
|
|
1168
|
+
ancestors.push(current);
|
|
1169
|
+
if (current === root) {
|
|
1170
|
+
return isPathBlocked(ancestors, stopElements) ? UNSCOPED_PROXIMITY : ancestors.length - 1;
|
|
1171
|
+
}
|
|
746
1172
|
}
|
|
747
|
-
|
|
748
|
-
|
|
1173
|
+
return UNSCOPED_PROXIMITY;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
function isPathBlocked(ancestors, stopElements) {
|
|
1177
|
+
return Boolean(stopElements) && ancestors.some(ancestor => stopElements.has(ancestor));
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
function getNestedSelector(selector, ancestorsSelectors, docContext) {
|
|
1181
|
+
if (!ancestorsSelectors || !ancestorsSelectors.length) {
|
|
1182
|
+
return null;
|
|
749
1183
|
}
|
|
750
|
-
|
|
1184
|
+
const nestedSelectorText = nestSelectorText(getSelectorText(selector.data, docContext), getNestingParentText(ancestorsSelectors, docContext), docContext);
|
|
1185
|
+
return { data: parseCss(nestedSelectorText, SELECTOR_LIST_CONTEXT) };
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
function getNestingParentText(ancestorsSelectors, docContext) {
|
|
1189
|
+
const prelude = ancestorsSelectors[ancestorsSelectors.length - 1];
|
|
1190
|
+
if (!docContext.nestingParentTexts.has(prelude)) {
|
|
1191
|
+
const grandParentText = ancestorsSelectors.length > 1 ? getNestingParentText(ancestorsSelectors.slice(0, -1), docContext) : null;
|
|
1192
|
+
const selectorTexts = [];
|
|
1193
|
+
for (let parentSelector = prelude.children.head; parentSelector; parentSelector = parentSelector.next) {
|
|
1194
|
+
if (!hasPseudoElement(parentSelector.data)) {
|
|
1195
|
+
const parentText = getSelectorText(parentSelector.data, docContext);
|
|
1196
|
+
selectorTexts.push(grandParentText === null ? parentText : nestSelectorText(parentText, grandParentText, docContext));
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
docContext.nestingParentTexts.set(prelude, selectorTexts.length ? IS_PSEUDO_CLASS_PREFIX + selectorTexts.join(PRELUDE_SEPARATOR) + IS_PSEUDO_CLASS_SUFFIX : NO_ELEMENT_SELECTOR);
|
|
1200
|
+
}
|
|
1201
|
+
return docContext.nestingParentTexts.get(prelude);
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
function hasPseudoElement(selector) {
|
|
1205
|
+
return Boolean(cssTree.find(selector, node => node.type === PSEUDO_ELEMENT_SELECTOR_TYPE || (node.type === PSEUDO_CLASS_SELECTOR_TYPE && PSEUDO_ELEMENT_SYNONYMS.has(decodeName(node.name)))));
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
function nestSelectorText(selectorText, parentText, docContext) {
|
|
1209
|
+
const selector = parseCss(selectorText);
|
|
1210
|
+
let hasNestingSelector = false;
|
|
1211
|
+
cssTree.walk(selector, {
|
|
1212
|
+
visit: NESTING_SELECTOR_TYPE,
|
|
1213
|
+
enter(_node, item, list) {
|
|
1214
|
+
hasNestingSelector = true;
|
|
1215
|
+
list.insertData(getNestingParentNode(parentText, docContext), item);
|
|
1216
|
+
list.remove(item);
|
|
1217
|
+
}
|
|
1218
|
+
});
|
|
1219
|
+
if (hasNestingSelector) {
|
|
1220
|
+
return cssTree.generate(selector);
|
|
1221
|
+
}
|
|
1222
|
+
const startsWithCombinator = selector.children.head.data.type === COMBINATOR_NAME;
|
|
1223
|
+
return parentText + (startsWithCombinator ? EMPTY_STRING : DESCENDANT_COMBINATOR) + selectorText;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
function getNestingParentNode(parentText, docContext) {
|
|
1227
|
+
if (!docContext.nestingParentNodes.has(parentText)) {
|
|
1228
|
+
docContext.nestingParentNodes.set(parentText, parseCss(parentText).children.head.data);
|
|
1229
|
+
}
|
|
1230
|
+
return cssTree.clone(docContext.nestingParentNodes.get(parentText));
|
|
751
1231
|
}
|
|
752
1232
|
|
|
753
1233
|
function compareDeclarations(declarationA, declarationB, docContext) {
|
|
@@ -762,7 +1242,7 @@ function compareDeclarations(declarationA, declarationB, docContext) {
|
|
|
762
1242
|
const selectorDataB = declarationB.selector ? docContext.selectorData.get(declarationB.selector) : null;
|
|
763
1243
|
if (selectorDataA && selectorDataB) {
|
|
764
1244
|
const layerComparison = compareLayers(selectorDataA.layerStack, selectorDataB.layerStack, docContext);
|
|
765
|
-
if (layerComparison
|
|
1245
|
+
if (layerComparison) {
|
|
766
1246
|
return importantA ? -layerComparison : layerComparison;
|
|
767
1247
|
}
|
|
768
1248
|
const specificityA = declarationA.specificity;
|
|
@@ -776,8 +1256,11 @@ function compareDeclarations(declarationA, declarationB, docContext) {
|
|
|
776
1256
|
if (specificityA.c !== specificityB.c) {
|
|
777
1257
|
return specificityA.c - specificityB.c;
|
|
778
1258
|
}
|
|
779
|
-
if (
|
|
780
|
-
return
|
|
1259
|
+
if (declarationA.proximity !== declarationB.proximity) {
|
|
1260
|
+
return declarationB.proximity - declarationA.proximity;
|
|
1261
|
+
}
|
|
1262
|
+
if (declarationA.order !== declarationB.order) {
|
|
1263
|
+
return declarationA.order - declarationB.order;
|
|
781
1264
|
}
|
|
782
1265
|
return 0;
|
|
783
1266
|
} else {
|
|
@@ -813,27 +1296,32 @@ function compareLayers(layersA, layersB, docContext) {
|
|
|
813
1296
|
if (fullLayerNameA === fullLayerNameB) {
|
|
814
1297
|
return 0;
|
|
815
1298
|
}
|
|
1299
|
+
const comparisonKey = fullLayerNameA + CONTEXT_KEY_SEPARATOR + fullLayerNameB;
|
|
1300
|
+
if (!docContext.layerComparisons.has(comparisonKey)) {
|
|
1301
|
+
docContext.layerComparisons.set(comparisonKey, compareLayerNames(layersA, layersB, docContext));
|
|
1302
|
+
}
|
|
1303
|
+
return docContext.layerComparisons.get(comparisonKey);
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
function compareLayerNames(layersA, layersB, docContext) {
|
|
816
1307
|
const minLength = Math.min(layersA.length, layersB.length);
|
|
817
|
-
const effectiveMap = docContext.layerOrder;
|
|
818
1308
|
for (let indexLayer = 0; indexLayer < minLength; indexLayer++) {
|
|
819
1309
|
if (layersA[indexLayer] !== layersB[indexLayer]) {
|
|
820
|
-
const
|
|
821
|
-
const
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
if (orderA !== undefined && orderB !== undefined) {
|
|
825
|
-
return orderA - orderB;
|
|
1310
|
+
const positionA = docContext.layerOrder.get(getFullLayerName(layersA.slice(0, indexLayer + 1)));
|
|
1311
|
+
const positionB = docContext.layerOrder.get(getFullLayerName(layersB.slice(0, indexLayer + 1)));
|
|
1312
|
+
if (!positionA || !positionB) {
|
|
1313
|
+
return UNCERTAIN_LAYER_ORDER;
|
|
826
1314
|
}
|
|
827
|
-
if (
|
|
1315
|
+
if (positionA.firstCertain < positionB.first) {
|
|
828
1316
|
return -1;
|
|
829
1317
|
}
|
|
830
|
-
if (
|
|
1318
|
+
if (positionB.firstCertain < positionA.first) {
|
|
831
1319
|
return 1;
|
|
832
1320
|
}
|
|
833
|
-
return
|
|
1321
|
+
return UNCERTAIN_LAYER_ORDER;
|
|
834
1322
|
}
|
|
835
1323
|
}
|
|
836
|
-
return
|
|
1324
|
+
return layersB.length - layersA.length;
|
|
837
1325
|
}
|
|
838
1326
|
|
|
839
1327
|
function removeStylesheetEmptyRules(cssRules, docContext) {
|
|
@@ -843,15 +1331,22 @@ function removeStylesheetEmptyRules(cssRules, docContext) {
|
|
|
843
1331
|
if (ruleData.type === RULE_TYPE) {
|
|
844
1332
|
if (hasChildNodes(ruleData.block)) {
|
|
845
1333
|
removeStylesheetEmptyRules(ruleData.block.children, docContext);
|
|
846
|
-
}
|
|
1334
|
+
}
|
|
1335
|
+
if (!hasChildNodes(ruleData.block)) {
|
|
847
1336
|
docContext.stats.discarded++;
|
|
848
1337
|
removedRules.add(cssRule);
|
|
849
1338
|
}
|
|
850
|
-
} else if (ruleData
|
|
1339
|
+
} else if (isImportRule(ruleData)) {
|
|
1340
|
+
removeStylesheetEmptyRules(ruleData.prelude.children.head.data.importedChildren, docContext);
|
|
1341
|
+
} else if (ruleData.type === AT_RULE_TYPE && ruleData.block && decodeName(ruleData.name) !== FONT_FACE_NAME && decodeName(ruleData.name) !== KEYFRAMES_NAME) {
|
|
851
1342
|
removeStylesheetEmptyRules(ruleData.block.children, docContext);
|
|
852
1343
|
if (!hasChildNodes(ruleData.block)) {
|
|
853
|
-
|
|
854
|
-
|
|
1344
|
+
if (decodeName(ruleData.name) === LAYER_NAME) {
|
|
1345
|
+
removeEmptyLayerRule(ruleData, cssRule, removedRules, docContext);
|
|
1346
|
+
} else {
|
|
1347
|
+
docContext.stats.discarded++;
|
|
1348
|
+
removedRules.add(cssRule);
|
|
1349
|
+
}
|
|
855
1350
|
}
|
|
856
1351
|
}
|
|
857
1352
|
}
|
|
@@ -904,22 +1399,18 @@ function expandRawCssRules(ruleData) {
|
|
|
904
1399
|
const ruleChildren = [];
|
|
905
1400
|
if (hasChildNodes(ruleData.block)) {
|
|
906
1401
|
for (let cssRuleNode = ruleData.block.children.head; cssRuleNode; cssRuleNode = cssRuleNode.next) {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
console.warn(PARSE_CSS_ERROR_MESSAGE, cssRuleNode.data.value, error);
|
|
919
|
-
}
|
|
1402
|
+
const rawRuleText = getRawRuleText(cssRuleNode.data);
|
|
1403
|
+
if (rawRuleText !== null) {
|
|
1404
|
+
try {
|
|
1405
|
+
const stylesheet = parseCss(rawRuleText, STYLESHEET_CONTEXT);
|
|
1406
|
+
for (let stylesheetChild = stylesheet.children.head; stylesheetChild; stylesheetChild = stylesheetChild.next) {
|
|
1407
|
+
ruleChildren.push(stylesheetChild);
|
|
1408
|
+
}
|
|
1409
|
+
} catch (error) {
|
|
1410
|
+
if (DEBUG) {
|
|
1411
|
+
// eslint-disable-next-line no-console
|
|
1412
|
+
console.warn(PARSE_CSS_ERROR_MESSAGE, rawRuleText, error);
|
|
920
1413
|
}
|
|
921
|
-
} else {
|
|
922
|
-
ruleChildren.push(cssRuleNode);
|
|
923
1414
|
}
|
|
924
1415
|
} else {
|
|
925
1416
|
ruleChildren.push(cssRuleNode);
|
|
@@ -930,62 +1421,19 @@ function expandRawCssRules(ruleData) {
|
|
|
930
1421
|
ruleChildren.forEach(ruleChild => ruleData.block.children.appendData(ruleChild.data));
|
|
931
1422
|
}
|
|
932
1423
|
|
|
933
|
-
function
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
return selectorText;
|
|
937
|
-
} else {
|
|
938
|
-
let contexts = [EMPTY_STRING];
|
|
939
|
-
ancestorsSelectors.forEach(selectorList => {
|
|
940
|
-
if (hasChildNodes(selectorList)) {
|
|
941
|
-
const parentSelectors = selectorList.children.toArray();
|
|
942
|
-
const nextContexts = [];
|
|
943
|
-
contexts.forEach(context => parentSelectors.forEach(parentSelector => {
|
|
944
|
-
const parentText = getSelectorText(parentSelector, docContext);
|
|
945
|
-
const combined = context ? combineSelectors(context, parentText) : parentText;
|
|
946
|
-
if (!nextContexts.includes(combined)) {
|
|
947
|
-
nextContexts.push(combined);
|
|
948
|
-
}
|
|
949
|
-
}));
|
|
950
|
-
if (nextContexts.length) {
|
|
951
|
-
contexts = nextContexts;
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
});
|
|
955
|
-
const expandedSelectors = new Set();
|
|
956
|
-
contexts.forEach(context => {
|
|
957
|
-
const result = context ? combineSelectors(context, selectorText) : selectorText;
|
|
958
|
-
expandedSelectors.add(result);
|
|
959
|
-
});
|
|
960
|
-
return Array.from(expandedSelectors).join(PRELUDE_SEPARATOR);
|
|
1424
|
+
function getRawRuleText(node) {
|
|
1425
|
+
if (node.type === RAW_TYPE && holdsBlock(node.value)) {
|
|
1426
|
+
return node.value;
|
|
961
1427
|
}
|
|
1428
|
+
if (node.type === DECLARATION_TYPE && !node.property.startsWith(CUSTOM_PROPERTY_PREFIX) && node.value.type === RAW_TYPE && holdsBlock(node.value.value) && node.value.value.trimStart().indexOf(BLOCK_OPEN) > 0) {
|
|
1429
|
+
return node.property + DECLARATION_SEPARATOR + node.value.value;
|
|
1430
|
+
}
|
|
1431
|
+
return null;
|
|
962
1432
|
}
|
|
963
1433
|
|
|
964
|
-
function
|
|
965
|
-
const
|
|
966
|
-
|
|
967
|
-
let hasNesting = false;
|
|
968
|
-
cssTree.walk(childSelector, {
|
|
969
|
-
visit: NESTING_SELECTOR_TYPE,
|
|
970
|
-
enter(_node, item, list) {
|
|
971
|
-
hasNesting = true;
|
|
972
|
-
if (!parentSelector) {
|
|
973
|
-
list.remove(item);
|
|
974
|
-
return;
|
|
975
|
-
}
|
|
976
|
-
const nodes = parentSelector.children.toArray().map(parent => cssTree.clone(parent));
|
|
977
|
-
nodes.forEach(node => list.insertData(node, item));
|
|
978
|
-
list.remove(item);
|
|
979
|
-
}
|
|
980
|
-
});
|
|
981
|
-
if (hasNesting) {
|
|
982
|
-
return cssTree.generate(childSelector);
|
|
983
|
-
}
|
|
984
|
-
if (!parentSelector) {
|
|
985
|
-
return cssTree.generate(childSelector);
|
|
986
|
-
}
|
|
987
|
-
const combinedSelector = parseCss(`${parentSelectorText} ${childSelectorText}`);
|
|
988
|
-
return cssTree.generate(combinedSelector);
|
|
1434
|
+
function holdsBlock(text) {
|
|
1435
|
+
const blockOpenIndex = text.indexOf(BLOCK_OPEN);
|
|
1436
|
+
return blockOpenIndex !== -1 && blockOpenIndex < text.indexOf(BLOCK_CLOSE);
|
|
989
1437
|
}
|
|
990
1438
|
|
|
991
1439
|
function hasChildNodes(node) {
|
|
@@ -1011,7 +1459,7 @@ function getPreludeText(prelude, docContext) {
|
|
|
1011
1459
|
}
|
|
1012
1460
|
|
|
1013
1461
|
function getFullLayerName(layers) {
|
|
1014
|
-
return layers.
|
|
1462
|
+
return layers.join(LAYER_NAME_KEY_SEPARATOR);
|
|
1015
1463
|
}
|
|
1016
1464
|
|
|
1017
1465
|
function parseCss(text, context = SELECTOR_CONTEXT) {
|