single-file-core 1.6.9 → 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 +663 -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";
|
|
@@ -51,35 +55,58 @@ const COMBINATOR_NAME = "Combinator";
|
|
|
51
55
|
const TYPE_SELECTOR_TYPE = "TypeSelector";
|
|
52
56
|
const STYLE_ATTRIBUTE_NAME = "style";
|
|
53
57
|
const SELECTOR_LIST_CONTEXT = "selectorList";
|
|
58
|
+
const SCOPE_PRELUDE_OPTIONS = { context: "atrulePrelude", atrule: SCOPE_NAME };
|
|
54
59
|
const STYLESHEET_CONTEXT = "stylesheet";
|
|
55
60
|
const SELECTOR_CONTEXT = "selector";
|
|
56
61
|
const DECLARATION_LIST_CONTEXT = "declarationList";
|
|
57
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";
|
|
58
69
|
const PARSE_CSS_ERROR_MESSAGE = "Failed to parse CSS";
|
|
59
70
|
const QSA_ERROR_MESSAGE = "Failed to match selector";
|
|
60
71
|
const PRELUDE_SEPARATOR = ",";
|
|
61
|
-
const
|
|
72
|
+
const DECLARATION_SEPARATOR = ":";
|
|
73
|
+
const IS_PSEUDO_CLASS_PREFIX = ":is(";
|
|
74
|
+
const IS_PSEUDO_CLASS_SUFFIX = ")";
|
|
75
|
+
const NO_ELEMENT_SELECTOR = ":not(*)";
|
|
62
76
|
const SCOPE_PSEUDO_CLASS = ":scope";
|
|
77
|
+
const SCOPE_PSEUDO_CLASS_NAME = "scope";
|
|
78
|
+
const DESCENDANT_COMBINATOR = " ";
|
|
63
79
|
const NAMESPACE_SEPARATOR = "|";
|
|
64
80
|
const SELECTOR_SUPPORTS_PREFIX = "selector(";
|
|
65
81
|
const SELECTOR_SUPPORTS_SUFFIX = ")";
|
|
66
82
|
const VENDOR_PREFIX = "-";
|
|
67
83
|
const CUSTOM_PROPERTY_PREFIX = "--";
|
|
68
84
|
const LAYER_NAME_SEPARATOR = ".";
|
|
69
|
-
const
|
|
85
|
+
const LAYER_NAME_KEY_SEPARATOR = "\u0000";
|
|
70
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;
|
|
71
93
|
const BLOCK_OPEN = "{";
|
|
72
94
|
const BLOCK_CLOSE = "}";
|
|
73
95
|
const EMPTY_STRING = "";
|
|
74
96
|
const CSS_IMPORTANCE_NOT_IMPORTANT = 0;
|
|
75
97
|
const CSS_IMPORTANCE_IMPORTANT = 1;
|
|
76
98
|
const INVALID_CSS_ESCAPE_TEST = /\\(?![0-9a-fA-F]{1,6}\s|[^0-9a-zA-Z])/;
|
|
77
|
-
const
|
|
99
|
+
const ANONYMOUS_LAYER_PREFIX = "\u0000";
|
|
100
|
+
const UNCERTAIN_LAYER_ORDER = null;
|
|
101
|
+
const UNDECLARED_LAYER_POSITION = Infinity;
|
|
78
102
|
|
|
79
103
|
export {
|
|
80
104
|
process,
|
|
81
105
|
isUnsupportedPropertyValue,
|
|
82
|
-
|
|
106
|
+
getValueValidity,
|
|
107
|
+
VALIDITY_VALID,
|
|
108
|
+
VALIDITY_UNKNOWN,
|
|
109
|
+
VALIDITY_INVALID
|
|
83
110
|
};
|
|
84
111
|
|
|
85
112
|
function isUnsupportedPropertyValue(property, value) {
|
|
@@ -87,28 +114,41 @@ function isUnsupportedPropertyValue(property, value) {
|
|
|
87
114
|
return Boolean(!match.matched && match.error && match.error.name !== UNKNOWN_PROPERTY_ERROR_NAME);
|
|
88
115
|
}
|
|
89
116
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// the saved CSS. Four of the five sites in a fifteen-site sweep that use line-clamp were affected.
|
|
96
|
-
function isUnsupportedVendorValue(property, name) {
|
|
97
|
-
if (!name.startsWith(VENDOR_PREFIX)) {
|
|
98
|
-
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;
|
|
99
122
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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;
|
|
104
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;
|
|
105
140
|
try {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
// a value CSS.supports will not even take as an argument tells us nothing either way
|
|
110
|
-
return false;
|
|
141
|
+
match = cssTree.lexer.matchProperty(property, value);
|
|
142
|
+
} catch {
|
|
143
|
+
return VALIDITY_UNKNOWN;
|
|
111
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;
|
|
150
|
+
}
|
|
151
|
+
return VALIDITY_UNKNOWN;
|
|
112
152
|
}
|
|
113
153
|
|
|
114
154
|
function process(doc, stylesheets) {
|
|
@@ -116,20 +156,30 @@ function process(doc, stylesheets) {
|
|
|
116
156
|
doc,
|
|
117
157
|
stats: { processed: 0, discarded: 0 },
|
|
118
158
|
matchedElements: new Set(),
|
|
159
|
+
revertLayerElements: new Set(),
|
|
160
|
+
revertLayerRules: new WeakMap(),
|
|
161
|
+
revertLayerDeclarations: new WeakMap(),
|
|
162
|
+
hasIndeterminateRevertLayer: false,
|
|
119
163
|
matchedSelectors: new Map(),
|
|
120
164
|
matchingSelectors: new Map(),
|
|
165
|
+
scopeProximities: new Map(),
|
|
166
|
+
scopeProximityKeys: new Map(),
|
|
121
167
|
layerDeclarationCounter: 0,
|
|
122
|
-
|
|
168
|
+
anonymousLayerCounter: 0,
|
|
123
169
|
layerOrder: new Map(),
|
|
170
|
+
layerComparisons: new Map(),
|
|
124
171
|
selectorData: new Map(),
|
|
125
172
|
selectorTexts: new Map(),
|
|
173
|
+
nestingParentTexts: new Map(),
|
|
174
|
+
nestingParentNodes: new Map(),
|
|
175
|
+
scopedSelectorTexts: new Map(),
|
|
126
176
|
supportedSelectors: new Map(),
|
|
177
|
+
valueValidities: new Map(),
|
|
127
178
|
preludeTexts: new Map(),
|
|
128
179
|
rulesCounter: 0,
|
|
129
180
|
scopeIdCounter: 0
|
|
130
181
|
};
|
|
131
182
|
collectLayerOrder(stylesheets, docContext);
|
|
132
|
-
buildEffectiveLayerOrder(docContext);
|
|
133
183
|
minifyRules(stylesheets, docContext);
|
|
134
184
|
computeCascade(docContext);
|
|
135
185
|
removeEmptyRules(stylesheets, docContext);
|
|
@@ -140,36 +190,27 @@ function collectLayerOrder(stylesheets, docContext) {
|
|
|
140
190
|
stylesheets.forEach((stylesheetInfo, key) => {
|
|
141
191
|
if (!stylesheetInfo.scoped && stylesheetInfo.stylesheet && !key.urlNode) {
|
|
142
192
|
if (hasChildNodes(stylesheetInfo.stylesheet)) {
|
|
143
|
-
collectStylesheetLayerOrder(stylesheetInfo.stylesheet.children, { layerStack: [], conditionalStack:
|
|
193
|
+
collectStylesheetLayerOrder(stylesheetInfo.stylesheet.children, { layerStack: [], conditionalStack: getTopConditionalStack(stylesheetInfo) }, docContext);
|
|
144
194
|
}
|
|
145
195
|
}
|
|
146
196
|
});
|
|
147
197
|
}
|
|
148
198
|
|
|
149
|
-
function
|
|
150
|
-
|
|
151
|
-
for (let indexDeclaration = 0; indexDeclaration < docContext.layerDeclarations.length; indexDeclaration++) {
|
|
152
|
-
const declaration = docContext.layerDeclarations[indexDeclaration];
|
|
153
|
-
layerNames.push(declaration.name);
|
|
154
|
-
}
|
|
155
|
-
for (let indexLayerName = 0; indexLayerName < layerNames.length; indexLayerName++) {
|
|
156
|
-
const name = layerNames[indexLayerName];
|
|
157
|
-
if (!docContext.layerOrder.has(name)) {
|
|
158
|
-
docContext.layerOrder.set(name, docContext.layerOrder.size);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
199
|
+
function getTopConditionalStack(stylesheetInfo) {
|
|
200
|
+
return stylesheetInfo.mediaText ? [{ name: MEDIA_AT_RULE_NAME, prelude: stylesheetInfo.mediaText }] : [];
|
|
161
201
|
}
|
|
162
202
|
|
|
163
203
|
function minifyRules(stylesheets, docContext) {
|
|
164
204
|
stylesheets.forEach((stylesheetInfo, key) => {
|
|
165
205
|
if (!stylesheetInfo.scoped && stylesheetInfo.stylesheet && !key.urlNode) {
|
|
166
206
|
if (hasChildNodes(stylesheetInfo.stylesheet)) {
|
|
167
|
-
const topConditionalStack = stylesheetInfo.mediaText ? [{ name: MEDIA_AT_RULE_NAME, prelude: stylesheetInfo.mediaText }] : [];
|
|
168
207
|
minifyStylesheetRules(stylesheetInfo.stylesheet.children, stylesheets, {
|
|
169
208
|
ancestorsSelectors: [],
|
|
170
209
|
layerStack: [],
|
|
171
210
|
scopeStack: [],
|
|
172
|
-
conditionalStack:
|
|
211
|
+
conditionalStack: getTopConditionalStack(stylesheetInfo),
|
|
212
|
+
ownerElement: getStylesheetOwnerElement(key),
|
|
213
|
+
hasDefaultNamespace: hasDefaultNamespace(stylesheetInfo.stylesheet.children)
|
|
173
214
|
}, docContext);
|
|
174
215
|
}
|
|
175
216
|
}
|
|
@@ -179,7 +220,9 @@ function minifyRules(stylesheets, docContext) {
|
|
|
179
220
|
function computeCascade(docContext) {
|
|
180
221
|
const winningDeclarations = new Set();
|
|
181
222
|
docContext.matchedElements.forEach(element => computeCascadedStylesForElement(element, winningDeclarations, docContext));
|
|
182
|
-
|
|
223
|
+
if (!docContext.hasIndeterminateRevertLayer) {
|
|
224
|
+
removeLosingDeclarations(winningDeclarations, docContext);
|
|
225
|
+
}
|
|
183
226
|
}
|
|
184
227
|
|
|
185
228
|
function removeEmptyRules(stylesheets, docContext) {
|
|
@@ -196,8 +239,10 @@ function collectStylesheetLayerOrder(cssRules, layerContext, docContext) {
|
|
|
196
239
|
const { layerStack, conditionalStack } = layerContext;
|
|
197
240
|
for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
|
|
198
241
|
const ruleData = cssRule.data;
|
|
199
|
-
if (ruleData.type === AT_RULE_TYPE && ruleData.name === LAYER_NAME) {
|
|
242
|
+
if (ruleData.type === AT_RULE_TYPE && decodeName(ruleData.name) === LAYER_NAME) {
|
|
200
243
|
collectStylesheetLayerRule(ruleData, layerStack, conditionalStack, docContext);
|
|
244
|
+
} else if (isImportRule(ruleData)) {
|
|
245
|
+
collectImportLayerOrder(ruleData, layerContext, docContext);
|
|
201
246
|
} else if (ruleData.type === AT_RULE_TYPE && hasChildNodes(ruleData.block)) {
|
|
202
247
|
const newConditionalStack = buildConditionalStack(conditionalStack, ruleData, docContext);
|
|
203
248
|
collectStylesheetLayerOrder(ruleData.block.children, { layerStack, conditionalStack: newConditionalStack }, docContext);
|
|
@@ -209,48 +254,136 @@ function collectStylesheetLayerOrder(cssRules, layerContext, docContext) {
|
|
|
209
254
|
|
|
210
255
|
function collectStylesheetLayerRule(ruleData, layerStack, conditionalStack, docContext) {
|
|
211
256
|
if (ruleData.block) {
|
|
212
|
-
const
|
|
213
|
-
registerLayerDeclaration(layerStack,
|
|
214
|
-
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);
|
|
215
260
|
} else if (ruleData.prelude) {
|
|
216
|
-
|
|
217
|
-
|
|
261
|
+
getPreludeText(ruleData.prelude, docContext).split(PRELUDE_SEPARATOR).forEach(layerName => registerLayerDeclaration(layerStack, splitLayerName(layerName), conditionalStack, docContext));
|
|
262
|
+
}
|
|
263
|
+
}
|
|
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)];
|
|
218
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++;
|
|
219
306
|
}
|
|
220
307
|
|
|
221
308
|
function buildConditionalStack(conditionalStack, ruleData, docContext) {
|
|
222
|
-
const isConditional = CONDITIONAL_AT_RULE_NAMES.has(ruleData.name);
|
|
309
|
+
const isConditional = CONDITIONAL_AT_RULE_NAMES.has(decodeName(ruleData.name));
|
|
223
310
|
return isConditional
|
|
224
311
|
? [...conditionalStack, { name: ruleData.name, prelude: getPreludeText(ruleData.prelude, docContext) }]
|
|
225
312
|
: conditionalStack;
|
|
226
313
|
}
|
|
227
314
|
|
|
228
|
-
function
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
name:
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
+
}
|
|
235
354
|
}
|
|
236
355
|
|
|
237
356
|
function minifyStylesheetRules(cssRules, stylesheets, processingContext, docContext) {
|
|
238
357
|
const removedRules = new Set();
|
|
358
|
+
let nestedRuleSeen = false;
|
|
359
|
+
let declarationsOrder;
|
|
239
360
|
for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
|
|
240
361
|
docContext.stats.processed++;
|
|
241
|
-
|
|
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
|
+
}
|
|
242
374
|
}
|
|
243
375
|
removedRules.forEach(cssRule => cssRules.remove(cssRule));
|
|
244
376
|
}
|
|
245
377
|
|
|
246
378
|
function minifyRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
|
|
247
|
-
|
|
379
|
+
const atRuleName = decodeName(ruleData.name);
|
|
380
|
+
if (isImportRule(ruleData)) {
|
|
248
381
|
minifyImportRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
249
|
-
} else if (ruleData.type === AT_RULE_TYPE &&
|
|
382
|
+
} else if (ruleData.type === AT_RULE_TYPE && atRuleName === LAYER_NAME && hasChildNodes(ruleData.block)) {
|
|
250
383
|
minifyLayerRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
251
|
-
} else if (ruleData.type === AT_RULE_TYPE &&
|
|
384
|
+
} else if (ruleData.type === AT_RULE_TYPE && atRuleName === SCOPE_NAME && hasChildNodes(ruleData.block)) {
|
|
252
385
|
minifyScopeRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
253
|
-
} 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)) {
|
|
254
387
|
minifyAtRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
255
388
|
} else if (ruleData.type === RULE_TYPE && hasChildNodes(ruleData.prelude)) {
|
|
256
389
|
minifyStylesheetRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
@@ -259,34 +392,52 @@ function minifyRule(ruleData, cssRule, stylesheets, processingContext, removedRu
|
|
|
259
392
|
|
|
260
393
|
function minifyImportRule(ruleData, _cssRule, stylesheets, processingContext, _removedRules, docContext) {
|
|
261
394
|
const urlNode = ruleData.prelude.children.head.data;
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
if (urlNode.importedSupportsCondition !== undefined) {
|
|
267
|
-
topConditionalStack.push({ name: SUPPORTS_AT_RULE_NAME, prelude: urlNode.importedSupportsCondition });
|
|
268
|
-
}
|
|
395
|
+
const conditionalStack = buildImportConditionalStack(processingContext.conditionalStack, urlNode);
|
|
396
|
+
const layerSegments = getImportLayerSegments(ruleData, urlNode, docContext);
|
|
397
|
+
const layerStack = layerSegments ? [...processingContext.layerStack, ...layerSegments] : processingContext.layerStack;
|
|
269
398
|
minifyStylesheetRules(urlNode.importedChildren, stylesheets, {
|
|
270
399
|
...processingContext,
|
|
271
|
-
|
|
400
|
+
layerStack,
|
|
401
|
+
conditionalStack,
|
|
402
|
+
hasDefaultNamespace: processingContext.hasDefaultNamespace || hasDefaultNamespace(urlNode.importedChildren)
|
|
272
403
|
}, docContext);
|
|
273
404
|
}
|
|
274
405
|
|
|
275
406
|
function minifyLayerRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
|
|
276
|
-
const
|
|
277
|
-
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 };
|
|
278
413
|
expandRawCssRules(ruleData);
|
|
279
414
|
minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
|
|
280
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 {
|
|
281
424
|
docContext.stats.discarded++;
|
|
282
425
|
removedRules.add(cssRule);
|
|
283
426
|
}
|
|
284
427
|
}
|
|
285
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
|
+
|
|
286
437
|
function minifyScopeRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
|
|
287
438
|
let scopeContext;
|
|
288
439
|
try {
|
|
289
|
-
const parsedPrelude = parsePrelude(ruleData
|
|
440
|
+
const parsedPrelude = parsePrelude(getScopePrelude(ruleData));
|
|
290
441
|
scopeContext = buildScopeContext(parsedPrelude, processingContext, docContext);
|
|
291
442
|
} catch (error) {
|
|
292
443
|
if (DEBUG) {
|
|
@@ -301,7 +452,9 @@ function minifyScopeRule(ruleData, cssRule, stylesheets, processingContext, remo
|
|
|
301
452
|
}
|
|
302
453
|
const newProcessingContext = {
|
|
303
454
|
...processingContext,
|
|
304
|
-
scopeStack: [...(processingContext.scopeStack || []), scopeContext]
|
|
455
|
+
scopeStack: [...(processingContext.scopeStack || []), scopeContext],
|
|
456
|
+
hasUnqueryableSelector: processingContext.hasUnqueryableSelector || scopeContext.hasUnqueryableSelector,
|
|
457
|
+
hasNestedUnqueryablePseudoClass: processingContext.hasNestedUnqueryablePseudoClass || scopeContext.hasNestedUnqueryablePseudoClass
|
|
305
458
|
};
|
|
306
459
|
expandRawCssRules(ruleData);
|
|
307
460
|
minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
|
|
@@ -314,50 +467,102 @@ function minifyScopeRule(ruleData, cssRule, stylesheets, processingContext, remo
|
|
|
314
467
|
function buildScopeContext(parsedPrelude, processingContext, docContext) {
|
|
315
468
|
const scopeStack = processingContext.scopeStack || [];
|
|
316
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);
|
|
317
473
|
let rootElements = [];
|
|
318
474
|
if (includeSelectors.length) {
|
|
319
475
|
rootElements = collectScopeRootElements(includeSelectors, scopeStack, docContext);
|
|
320
|
-
|
|
321
|
-
|
|
476
|
+
if (!rootElements.length && includeAnalysis.hasNestedUnqueryablePseudoClass) {
|
|
477
|
+
rootElements = scopeStack.length ? Array.from(scopeStack[scopeStack.length - 1].rootElements) : getDefaultScopeRoots(docContext);
|
|
478
|
+
}
|
|
322
479
|
} else {
|
|
323
|
-
rootElements =
|
|
480
|
+
rootElements = getImplicitScopeRoots(processingContext.ownerElement, docContext);
|
|
324
481
|
}
|
|
325
482
|
const uniqueRoots = Array.from(new Set(rootElements.filter(Boolean)));
|
|
326
483
|
if (!uniqueRoots.length) {
|
|
327
484
|
return null;
|
|
328
485
|
}
|
|
329
|
-
const boundaryElements = collectScopeBoundaryElements(
|
|
486
|
+
const boundaryElements = collectScopeBoundaryElements(excludeSelectors, uniqueRoots, docContext, processingContext.hasDefaultNamespace);
|
|
330
487
|
return {
|
|
331
488
|
id: docContext.scopeIdCounter++,
|
|
332
489
|
rootElements: new Set(uniqueRoots),
|
|
333
490
|
stopElements: boundaryElements,
|
|
491
|
+
hasUnqueryableSelector: includeAnalysis.hasUnqueryableSelector || excludeAnalysis.hasUnqueryableSelector,
|
|
492
|
+
hasNestedUnqueryablePseudoClass: includeAnalysis.hasNestedUnqueryablePseudoClass
|
|
334
493
|
};
|
|
335
494
|
}
|
|
336
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
|
+
|
|
337
506
|
function collectScopeRootElements(includeSelectors, scopeStack, docContext) {
|
|
338
507
|
const roots = new Set();
|
|
339
508
|
includeSelectors.forEach(selectorInfo => {
|
|
340
|
-
const selectorText = sanitizeSelector(selectorInfo,
|
|
341
|
-
const matchedNodes =
|
|
509
|
+
const selectorText = sanitizeSelector(selectorInfo, docContext);
|
|
510
|
+
const matchedNodes = scopeStack.length
|
|
511
|
+
? matchElementsInScope(getScopedSelectorText(selectorText, docContext), scopeStack)
|
|
512
|
+
: querySelectorAll(docContext.doc, selectorText);
|
|
342
513
|
filterElementsByScopes(matchedNodes, scopeStack).forEach(match => roots.add(match));
|
|
343
514
|
});
|
|
344
515
|
return Array.from(roots);
|
|
345
516
|
}
|
|
346
517
|
|
|
347
|
-
function collectScopeBoundaryElements(excludeSelectors, rootElements, docContext) {
|
|
348
|
-
const boundaries = new
|
|
518
|
+
function collectScopeBoundaryElements(excludeSelectors, rootElements, docContext, defaultNamespace) {
|
|
519
|
+
const boundaries = new Map();
|
|
349
520
|
if (!excludeSelectors.length || !rootElements.length) {
|
|
350
521
|
return boundaries;
|
|
351
522
|
}
|
|
352
523
|
excludeSelectors.forEach(selectorInfo => {
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
+
}
|
|
357
535
|
});
|
|
358
536
|
return boundaries;
|
|
359
537
|
}
|
|
360
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
|
+
|
|
361
566
|
function getDefaultScopeRoots(docContext) {
|
|
362
567
|
const roots = [];
|
|
363
568
|
if (docContext.doc && docContext.doc.documentElement) {
|
|
@@ -388,7 +593,8 @@ function minifyStylesheetRule(ruleData, cssRule, stylesheets, processingContext,
|
|
|
388
593
|
if (!isPreludeSupported(ruleData.prelude, docContext)) {
|
|
389
594
|
return;
|
|
390
595
|
}
|
|
391
|
-
|
|
596
|
+
expandRawCssRules(ruleData);
|
|
597
|
+
const removedSelectors = getRemovableSelectors(ruleData, processSelectors(ruleData, processingContext, docContext), docContext);
|
|
392
598
|
const wasDiscarded = removeUnmatchedSelectors(ruleData, removedSelectors, removedRules, cssRule, docContext);
|
|
393
599
|
if (!wasDiscarded && hasChildNodes(ruleData.block)) {
|
|
394
600
|
processNestedRules(ruleData, stylesheets, processingContext, docContext);
|
|
@@ -399,32 +605,70 @@ function processSelectors(ruleData, processingContext, docContext) {
|
|
|
399
605
|
const removedSelectors = [];
|
|
400
606
|
const { ancestorsSelectors, scopeStack } = processingContext;
|
|
401
607
|
for (let selector = ruleData.prelude.children.head, selectorIndex = 0; selector; selector = selector.next, selectorIndex++) {
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
} = 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);
|
|
407
612
|
if (hasUnqueryableSelector) {
|
|
408
613
|
ruleData.hasUnqueryableSelector = true;
|
|
409
614
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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);
|
|
420
630
|
}
|
|
631
|
+
} else if (!hasNestedUnqueryablePseudoClass) {
|
|
632
|
+
removedSelectors.push(selector);
|
|
421
633
|
}
|
|
422
634
|
}
|
|
423
635
|
return removedSelectors;
|
|
424
636
|
}
|
|
425
637
|
|
|
426
|
-
function
|
|
427
|
-
|
|
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);
|
|
428
672
|
let hasNestedUnqueryablePseudoClass = false;
|
|
429
673
|
let startsWithCombinator = false;
|
|
430
674
|
let functionalPseudoClassDepth = 0;
|
|
@@ -436,7 +680,7 @@ function analyzeSelector(selector) {
|
|
|
436
680
|
hasNestedUnqueryablePseudoClass = true;
|
|
437
681
|
}
|
|
438
682
|
} else if (node.type === PSEUDO_CLASS_SELECTOR_TYPE) {
|
|
439
|
-
if (PSEUDO_ELEMENT_SYNONYMS.has(node.name) || matchUnqueryablePseudoClass(node)) {
|
|
683
|
+
if (PSEUDO_ELEMENT_SYNONYMS.has(decodeName(node.name)) || matchUnqueryablePseudoClass(node)) {
|
|
440
684
|
hasUnqueryableSelector = true;
|
|
441
685
|
if (functionalPseudoClassDepth) {
|
|
442
686
|
hasNestedUnqueryablePseudoClass = true;
|
|
@@ -450,6 +694,11 @@ function analyzeSelector(selector) {
|
|
|
450
694
|
if (functionalPseudoClassDepth) {
|
|
451
695
|
hasNestedUnqueryablePseudoClass = true;
|
|
452
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
|
+
}
|
|
453
702
|
}
|
|
454
703
|
},
|
|
455
704
|
leave(node) {
|
|
@@ -516,28 +765,31 @@ function updateMatchingSelectors(matchedElements, selector, docContext) {
|
|
|
516
765
|
}
|
|
517
766
|
|
|
518
767
|
function processNestedRules(ruleData, stylesheets, processingContext, docContext) {
|
|
519
|
-
|
|
520
|
-
|
|
768
|
+
const newProcessingContext = {
|
|
769
|
+
...processingContext,
|
|
770
|
+
ancestorsSelectors: [...processingContext.ancestorsSelectors, ruleData.prelude],
|
|
771
|
+
hasUnqueryableSelector: Boolean(ruleData.hasUnqueryableSelector),
|
|
772
|
+
hasNestedUnqueryablePseudoClass: Boolean(ruleData.hasNestedUnqueryablePseudoClass)
|
|
773
|
+
};
|
|
521
774
|
minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
|
|
522
775
|
}
|
|
523
776
|
|
|
524
|
-
function registerSelector(selector, ruleData, processingContext, docContext) {
|
|
777
|
+
function registerSelector(selector, nestedSelector, ruleData, processingContext, docContext) {
|
|
525
778
|
const {
|
|
526
|
-
ancestorsSelectors,
|
|
527
779
|
layerStack,
|
|
528
780
|
scopeStack,
|
|
529
781
|
conditionalStack
|
|
530
782
|
} = processingContext;
|
|
531
783
|
docContext.selectorData.set(selector, {
|
|
532
|
-
specificity: computeMaxSpecificity(selector.data
|
|
784
|
+
specificity: computeMaxSpecificity(nestedSelector === null ? selector.data : nestedSelector.data),
|
|
533
785
|
rule: ruleData,
|
|
534
786
|
layerStack,
|
|
787
|
+
scopeStack,
|
|
535
788
|
conditionalStack
|
|
536
789
|
});
|
|
537
790
|
}
|
|
538
791
|
|
|
539
792
|
function computeCascadedStylesForElement(element, winningDeclarations, docContext) {
|
|
540
|
-
const cascadedStyles = new Map();
|
|
541
793
|
const allDeclarations = collectDeclarationItemsForElement(element, docContext);
|
|
542
794
|
const contextGroups = new Map();
|
|
543
795
|
allDeclarations.forEach(declarationData => {
|
|
@@ -549,18 +801,109 @@ function computeCascadedStylesForElement(element, winningDeclarations, docContex
|
|
|
549
801
|
}
|
|
550
802
|
contextGroups.get(contextKey).push(declarationData);
|
|
551
803
|
});
|
|
804
|
+
let hasWinningRevertLayer = false;
|
|
552
805
|
contextGroups.forEach(declarations => {
|
|
553
806
|
declarations.sort((declarationA, declarationB) => compareDeclarations(declarationA, declarationB, docContext));
|
|
807
|
+
const propertyDeclarations = new Map();
|
|
554
808
|
declarations.forEach(declarationData => {
|
|
555
|
-
const {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
});
|
|
809
|
+
const { property } = declarationData.declaration.data;
|
|
810
|
+
if (!propertyDeclarations.has(property)) {
|
|
811
|
+
propertyDeclarations.set(property, []);
|
|
812
|
+
}
|
|
813
|
+
propertyDeclarations.get(property).push(declarationData);
|
|
561
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;
|
|
562
853
|
});
|
|
563
|
-
|
|
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));
|
|
564
907
|
}
|
|
565
908
|
|
|
566
909
|
function createContextKey(conditionalStack) {
|
|
@@ -571,13 +914,15 @@ function collectDeclarationItemsForElement(element, docContext) {
|
|
|
571
914
|
const matchingSelectors = docContext.matchingSelectors.get(element);
|
|
572
915
|
const allDeclarations = [];
|
|
573
916
|
matchingSelectors.forEach(selector => {
|
|
574
|
-
const
|
|
917
|
+
const selectorData = docContext.selectorData.get(selector);
|
|
918
|
+
const cssRule = selectorData.rule;
|
|
575
919
|
if (hasChildNodes(cssRule.block)) {
|
|
920
|
+
const proximity = getScopeProximity(element, selector, docContext);
|
|
576
921
|
const declarations = cssRule.block.children;
|
|
577
922
|
for (let declaration = declarations.head; declaration; declaration = declaration.next) {
|
|
578
|
-
const { type, value } = declaration.data;
|
|
923
|
+
const { type, value, order } = declaration.data;
|
|
579
924
|
if (type === DECLARATION_TYPE && value) {
|
|
580
|
-
addDeclaration(declaration,
|
|
925
|
+
addDeclaration(declaration, selectorData.specificity, false, selector, order === undefined ? cssRule.order : order, proximity);
|
|
581
926
|
}
|
|
582
927
|
}
|
|
583
928
|
}
|
|
@@ -588,36 +933,41 @@ function collectDeclarationItemsForElement(element, docContext) {
|
|
|
588
933
|
}
|
|
589
934
|
return allDeclarations;
|
|
590
935
|
|
|
591
|
-
function addDeclaration(declaration, specificity, isInline, selector) {
|
|
936
|
+
function addDeclaration(declaration, specificity, isInline, selector, order, proximity) {
|
|
592
937
|
const { property, value } = declaration.data;
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
} catch {
|
|
606
|
-
// ignored
|
|
607
|
-
}
|
|
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
|
+
});
|
|
608
950
|
}
|
|
609
951
|
}
|
|
610
|
-
if (hasValueChildNodes && !isRawValue && !isInvalidValue) {
|
|
611
|
-
allDeclarations.push({
|
|
612
|
-
declaration,
|
|
613
|
-
selector,
|
|
614
|
-
specificity,
|
|
615
|
-
isInline
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
952
|
}
|
|
619
953
|
}
|
|
620
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
|
+
|
|
621
971
|
function getConditionalStackForSelector(selector, docContext) {
|
|
622
972
|
let conditionalStack = [];
|
|
623
973
|
if (selector) {
|
|
@@ -629,20 +979,27 @@ function getConditionalStackForSelector(selector, docContext) {
|
|
|
629
979
|
return conditionalStack;
|
|
630
980
|
}
|
|
631
981
|
|
|
632
|
-
function matchElements(selector,
|
|
633
|
-
let selectorText =
|
|
982
|
+
function matchElements(selector, nestedSelector, scopeStack, docContext, relativeToScope) {
|
|
983
|
+
let selectorText = sanitizeSelector(nestedSelector === null ? selector : nestedSelector, docContext);
|
|
634
984
|
if (relativeToScope) {
|
|
635
985
|
selectorText = SCOPE_PSEUDO_CLASS + selectorText;
|
|
986
|
+
} else if (scopeStack && scopeStack.length) {
|
|
987
|
+
selectorText = getScopedSelectorText(selectorText, docContext);
|
|
636
988
|
}
|
|
637
989
|
const cacheKey = createScopeCacheKey(selectorText, scopeStack);
|
|
990
|
+
if (scopeStack && scopeStack.length) {
|
|
991
|
+
docContext.scopeProximityKeys.set(selector, cacheKey);
|
|
992
|
+
}
|
|
638
993
|
const cachedNodes = docContext.matchedSelectors.get(cacheKey);
|
|
639
994
|
if (cachedNodes) {
|
|
640
995
|
return cachedNodes;
|
|
641
996
|
}
|
|
642
997
|
let nodes;
|
|
643
998
|
if (scopeStack && scopeStack.length) {
|
|
644
|
-
|
|
999
|
+
const proximities = new Map();
|
|
1000
|
+
nodes = matchElementsInScope(selectorText, scopeStack, proximities);
|
|
645
1001
|
nodes = filterElementsByScopes(nodes, scopeStack);
|
|
1002
|
+
docContext.scopeProximities.set(cacheKey, proximities);
|
|
646
1003
|
} else {
|
|
647
1004
|
nodes = querySelectorAll(docContext.doc, selectorText);
|
|
648
1005
|
}
|
|
@@ -650,6 +1007,26 @@ function matchElements(selector, ancestorsSelectors, scopeStack, docContext, rel
|
|
|
650
1007
|
return nodes;
|
|
651
1008
|
}
|
|
652
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
|
+
|
|
653
1030
|
function createScopeCacheKey(selectorText, scopeStack) {
|
|
654
1031
|
if (!scopeStack || !scopeStack.length) {
|
|
655
1032
|
return selectorText;
|
|
@@ -658,7 +1035,7 @@ function createScopeCacheKey(selectorText, scopeStack) {
|
|
|
658
1035
|
return `${selectorText}${CONTEXT_KEY_SEPARATOR}${signature}`;
|
|
659
1036
|
}
|
|
660
1037
|
|
|
661
|
-
function matchElementsInScope(selectorText, scopeStack) {
|
|
1038
|
+
function matchElementsInScope(selectorText, scopeStack, proximities) {
|
|
662
1039
|
const currentScope = scopeStack[scopeStack.length - 1];
|
|
663
1040
|
const roots = Array.from(currentScope.rootElements);
|
|
664
1041
|
if (!roots.length) {
|
|
@@ -666,7 +1043,19 @@ function matchElementsInScope(selectorText, scopeStack) {
|
|
|
666
1043
|
}
|
|
667
1044
|
const matchedNodes = new Set();
|
|
668
1045
|
roots.forEach(root => {
|
|
669
|
-
|
|
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
|
+
});
|
|
670
1059
|
});
|
|
671
1060
|
return Array.from(matchedNodes);
|
|
672
1061
|
}
|
|
@@ -674,7 +1063,7 @@ function matchElementsInScope(selectorText, scopeStack) {
|
|
|
674
1063
|
function matchSelectorWithinRoot(root, selectorText) {
|
|
675
1064
|
const matchedNodes = new Set();
|
|
676
1065
|
if (!root || root.nodeType !== 1) {
|
|
677
|
-
return
|
|
1066
|
+
return [];
|
|
678
1067
|
}
|
|
679
1068
|
if (matches(root, selectorText)) {
|
|
680
1069
|
matchedNodes.add(root);
|
|
@@ -736,17 +1125,10 @@ function getTraversalRoots(root) {
|
|
|
736
1125
|
return [];
|
|
737
1126
|
}
|
|
738
1127
|
if (root.nodeType === 9 || root.nodeType === 11) {
|
|
739
|
-
const roots = [];
|
|
740
1128
|
if (root.documentElement) {
|
|
741
|
-
|
|
1129
|
+
return [root.documentElement];
|
|
742
1130
|
}
|
|
743
|
-
|
|
744
|
-
roots.push(root.body);
|
|
745
|
-
}
|
|
746
|
-
if (!roots.length && root.children) {
|
|
747
|
-
roots.push(...Array.from(root.children).filter(node => node.nodeType === 1));
|
|
748
|
-
}
|
|
749
|
-
return roots;
|
|
1131
|
+
return root.children ? Array.from(root.children).filter(node => node.nodeType === 1) : [];
|
|
750
1132
|
}
|
|
751
1133
|
return [root];
|
|
752
1134
|
}
|
|
@@ -771,30 +1153,81 @@ function isElementWithinScopes(element, scopeStack) {
|
|
|
771
1153
|
}
|
|
772
1154
|
|
|
773
1155
|
function isElementWithinScope(element, scopeContext) {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
if (
|
|
777
|
-
return false;
|
|
778
|
-
}
|
|
779
|
-
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) {
|
|
780
1159
|
return true;
|
|
781
1160
|
}
|
|
782
|
-
current = current.parentElement;
|
|
783
1161
|
}
|
|
784
1162
|
return false;
|
|
785
1163
|
}
|
|
786
1164
|
|
|
787
|
-
function
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
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
|
+
}
|
|
1172
|
+
}
|
|
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;
|
|
793
1183
|
}
|
|
794
|
-
|
|
795
|
-
|
|
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);
|
|
796
1200
|
}
|
|
797
|
-
return
|
|
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));
|
|
798
1231
|
}
|
|
799
1232
|
|
|
800
1233
|
function compareDeclarations(declarationA, declarationB, docContext) {
|
|
@@ -809,7 +1242,7 @@ function compareDeclarations(declarationA, declarationB, docContext) {
|
|
|
809
1242
|
const selectorDataB = declarationB.selector ? docContext.selectorData.get(declarationB.selector) : null;
|
|
810
1243
|
if (selectorDataA && selectorDataB) {
|
|
811
1244
|
const layerComparison = compareLayers(selectorDataA.layerStack, selectorDataB.layerStack, docContext);
|
|
812
|
-
if (layerComparison
|
|
1245
|
+
if (layerComparison) {
|
|
813
1246
|
return importantA ? -layerComparison : layerComparison;
|
|
814
1247
|
}
|
|
815
1248
|
const specificityA = declarationA.specificity;
|
|
@@ -823,8 +1256,11 @@ function compareDeclarations(declarationA, declarationB, docContext) {
|
|
|
823
1256
|
if (specificityA.c !== specificityB.c) {
|
|
824
1257
|
return specificityA.c - specificityB.c;
|
|
825
1258
|
}
|
|
826
|
-
if (
|
|
827
|
-
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;
|
|
828
1264
|
}
|
|
829
1265
|
return 0;
|
|
830
1266
|
} else {
|
|
@@ -860,27 +1296,32 @@ function compareLayers(layersA, layersB, docContext) {
|
|
|
860
1296
|
if (fullLayerNameA === fullLayerNameB) {
|
|
861
1297
|
return 0;
|
|
862
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) {
|
|
863
1307
|
const minLength = Math.min(layersA.length, layersB.length);
|
|
864
|
-
const effectiveMap = docContext.layerOrder;
|
|
865
1308
|
for (let indexLayer = 0; indexLayer < minLength; indexLayer++) {
|
|
866
1309
|
if (layersA[indexLayer] !== layersB[indexLayer]) {
|
|
867
|
-
const
|
|
868
|
-
const
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
if (orderA !== undefined && orderB !== undefined) {
|
|
872
|
-
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;
|
|
873
1314
|
}
|
|
874
|
-
if (
|
|
1315
|
+
if (positionA.firstCertain < positionB.first) {
|
|
875
1316
|
return -1;
|
|
876
1317
|
}
|
|
877
|
-
if (
|
|
1318
|
+
if (positionB.firstCertain < positionA.first) {
|
|
878
1319
|
return 1;
|
|
879
1320
|
}
|
|
880
|
-
return
|
|
1321
|
+
return UNCERTAIN_LAYER_ORDER;
|
|
881
1322
|
}
|
|
882
1323
|
}
|
|
883
|
-
return
|
|
1324
|
+
return layersB.length - layersA.length;
|
|
884
1325
|
}
|
|
885
1326
|
|
|
886
1327
|
function removeStylesheetEmptyRules(cssRules, docContext) {
|
|
@@ -890,15 +1331,22 @@ function removeStylesheetEmptyRules(cssRules, docContext) {
|
|
|
890
1331
|
if (ruleData.type === RULE_TYPE) {
|
|
891
1332
|
if (hasChildNodes(ruleData.block)) {
|
|
892
1333
|
removeStylesheetEmptyRules(ruleData.block.children, docContext);
|
|
893
|
-
}
|
|
1334
|
+
}
|
|
1335
|
+
if (!hasChildNodes(ruleData.block)) {
|
|
894
1336
|
docContext.stats.discarded++;
|
|
895
1337
|
removedRules.add(cssRule);
|
|
896
1338
|
}
|
|
897
|
-
} 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) {
|
|
898
1342
|
removeStylesheetEmptyRules(ruleData.block.children, docContext);
|
|
899
1343
|
if (!hasChildNodes(ruleData.block)) {
|
|
900
|
-
|
|
901
|
-
|
|
1344
|
+
if (decodeName(ruleData.name) === LAYER_NAME) {
|
|
1345
|
+
removeEmptyLayerRule(ruleData, cssRule, removedRules, docContext);
|
|
1346
|
+
} else {
|
|
1347
|
+
docContext.stats.discarded++;
|
|
1348
|
+
removedRules.add(cssRule);
|
|
1349
|
+
}
|
|
902
1350
|
}
|
|
903
1351
|
}
|
|
904
1352
|
}
|
|
@@ -951,22 +1399,18 @@ function expandRawCssRules(ruleData) {
|
|
|
951
1399
|
const ruleChildren = [];
|
|
952
1400
|
if (hasChildNodes(ruleData.block)) {
|
|
953
1401
|
for (let cssRuleNode = ruleData.block.children.head; cssRuleNode; cssRuleNode = cssRuleNode.next) {
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
console.warn(PARSE_CSS_ERROR_MESSAGE, cssRuleNode.data.value, error);
|
|
966
|
-
}
|
|
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);
|
|
967
1413
|
}
|
|
968
|
-
} else {
|
|
969
|
-
ruleChildren.push(cssRuleNode);
|
|
970
1414
|
}
|
|
971
1415
|
} else {
|
|
972
1416
|
ruleChildren.push(cssRuleNode);
|
|
@@ -977,62 +1421,19 @@ function expandRawCssRules(ruleData) {
|
|
|
977
1421
|
ruleChildren.forEach(ruleChild => ruleData.block.children.appendData(ruleChild.data));
|
|
978
1422
|
}
|
|
979
1423
|
|
|
980
|
-
function
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
ancestorsSelectors.forEach(selectorList => {
|
|
987
|
-
if (hasChildNodes(selectorList)) {
|
|
988
|
-
const parentSelectors = selectorList.children.toArray();
|
|
989
|
-
const nextContexts = [];
|
|
990
|
-
contexts.forEach(context => parentSelectors.forEach(parentSelector => {
|
|
991
|
-
const parentText = getSelectorText(parentSelector, docContext);
|
|
992
|
-
const combined = context ? combineSelectors(context, parentText) : parentText;
|
|
993
|
-
if (!nextContexts.includes(combined)) {
|
|
994
|
-
nextContexts.push(combined);
|
|
995
|
-
}
|
|
996
|
-
}));
|
|
997
|
-
if (nextContexts.length) {
|
|
998
|
-
contexts = nextContexts;
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
});
|
|
1002
|
-
const expandedSelectors = new Set();
|
|
1003
|
-
contexts.forEach(context => {
|
|
1004
|
-
const result = context ? combineSelectors(context, selectorText) : selectorText;
|
|
1005
|
-
expandedSelectors.add(result);
|
|
1006
|
-
});
|
|
1007
|
-
return Array.from(expandedSelectors).join(PRELUDE_SEPARATOR);
|
|
1424
|
+
function getRawRuleText(node) {
|
|
1425
|
+
if (node.type === RAW_TYPE && holdsBlock(node.value)) {
|
|
1426
|
+
return node.value;
|
|
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;
|
|
1008
1430
|
}
|
|
1431
|
+
return null;
|
|
1009
1432
|
}
|
|
1010
1433
|
|
|
1011
|
-
function
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1014
|
-
let hasNesting = false;
|
|
1015
|
-
cssTree.walk(childSelector, {
|
|
1016
|
-
visit: NESTING_SELECTOR_TYPE,
|
|
1017
|
-
enter(_node, item, list) {
|
|
1018
|
-
hasNesting = true;
|
|
1019
|
-
if (!parentSelector) {
|
|
1020
|
-
list.remove(item);
|
|
1021
|
-
return;
|
|
1022
|
-
}
|
|
1023
|
-
const nodes = parentSelector.children.toArray().map(parent => cssTree.clone(parent));
|
|
1024
|
-
nodes.forEach(node => list.insertData(node, item));
|
|
1025
|
-
list.remove(item);
|
|
1026
|
-
}
|
|
1027
|
-
});
|
|
1028
|
-
if (hasNesting) {
|
|
1029
|
-
return cssTree.generate(childSelector);
|
|
1030
|
-
}
|
|
1031
|
-
if (!parentSelector) {
|
|
1032
|
-
return cssTree.generate(childSelector);
|
|
1033
|
-
}
|
|
1034
|
-
const combinedSelector = parseCss(`${parentSelectorText} ${childSelectorText}`);
|
|
1035
|
-
return cssTree.generate(combinedSelector);
|
|
1434
|
+
function holdsBlock(text) {
|
|
1435
|
+
const blockOpenIndex = text.indexOf(BLOCK_OPEN);
|
|
1436
|
+
return blockOpenIndex !== -1 && blockOpenIndex < text.indexOf(BLOCK_CLOSE);
|
|
1036
1437
|
}
|
|
1037
1438
|
|
|
1038
1439
|
function hasChildNodes(node) {
|
|
@@ -1058,7 +1459,7 @@ function getPreludeText(prelude, docContext) {
|
|
|
1058
1459
|
}
|
|
1059
1460
|
|
|
1060
1461
|
function getFullLayerName(layers) {
|
|
1061
|
-
return layers.
|
|
1462
|
+
return layers.join(LAYER_NAME_KEY_SEPARATOR);
|
|
1062
1463
|
}
|
|
1063
1464
|
|
|
1064
1465
|
function parseCss(text, context = SELECTOR_CONTEXT) {
|