single-file-core 1.5.47 → 1.5.49
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 +1 -4
- package/core/infobar.js +1 -1
- package/core/lib/processor-helper-inline.js +24 -0
- package/core/lib/processor-helper.js +15 -2
- package/core/util.js +2 -5
- package/modules/css-media-query-utils.js +63 -0
- package/modules/css-medias-alt-minifier.js +26 -23
- package/modules/css-rules-minifier.js +904 -79
- package/modules/css-scope-prelude-parser.js +119 -0
- package/modules/css-selector-sanitizer.js +106 -0
- package/modules/css-specificity.js +211 -0
- package/modules/html-serializer.js +14 -9
- package/modules/index.js +0 -2
- package/package.json +2 -2
- package/processors/compression/compression.js +182 -20
- package/processors/hooks/content/content-hooks-frames-web.js +2 -4
- package/processors/hooks/content/content-hooks-frames.js +6 -8
- package/vendor/css-font-property-parser.js +87 -67
- package/vendor/css-tree.js +9 -9
- package/modules/css-matched-rules.js +0 -384
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright 2010-
|
|
2
|
+
* Copyright 2010-2025 Gildas Lormeau
|
|
3
3
|
* contact : gildas.lormeau <at> gmail.com
|
|
4
4
|
*
|
|
5
5
|
* This file is part of SingleFile.
|
|
@@ -22,124 +22,949 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import * as cssTree from "./../vendor/css-tree.js";
|
|
25
|
+
import { computeMaxSpecificity } from "./css-specificity.js";
|
|
26
|
+
import { parsePrelude } from "./css-scope-prelude-parser.js";
|
|
27
|
+
import { sanitizeSelector } from "./css-selector-sanitizer.js";
|
|
25
28
|
|
|
26
29
|
const DEBUG = false;
|
|
27
30
|
|
|
31
|
+
const CANONICAL_PSEUDO_ELEMENT_NAMES = new Set(["after", "before", "first-letter", "first-line", "placeholder", "selection", "part", "marker"]);
|
|
32
|
+
const DYNAMIC_STATE_PSEUDO_CLASSES = new Set(["hover", "focus", "active", "focus-within", "focus-visible", "target", "visited", "link", "target-current"]);
|
|
33
|
+
const CONDITIONAL_AT_RULE_NAMES = new Set(["media", "supports", "container"]);
|
|
34
|
+
const RULE_TYPE = "Rule";
|
|
35
|
+
const AT_RULE_TYPE = "Atrule";
|
|
36
|
+
const NESTING_SELECTOR_TYPE = "NestingSelector";
|
|
37
|
+
const PSEUDO_CLASS_SELECTOR_TYPE = "PseudoClassSelector";
|
|
38
|
+
const DECLARATION_TYPE = "Declaration";
|
|
39
|
+
const RAW_TYPE = "Raw";
|
|
40
|
+
const PSEUDO_ELEMENT_SELECTOR_TYPE = "PseudoElementSelector";
|
|
41
|
+
const LAYER_NAME = "layer";
|
|
42
|
+
const SCOPE_NAME = "scope";
|
|
43
|
+
const IMPORT_NAME = "import";
|
|
44
|
+
const FONT_FACE_NAME = "font-face";
|
|
45
|
+
const KEYFRAMES_NAME = "keyframes";
|
|
46
|
+
const COMBINATOR_NAME = "Combinator";
|
|
47
|
+
const STYLE_ATTRIBUTE_NAME = "style";
|
|
48
|
+
const SELECTOR_LIST_CONTEXT = "selectorList";
|
|
49
|
+
const STYLESHEET_CONTEXT = "stylesheet";
|
|
50
|
+
const SELECTOR_CONTEXT = "selector";
|
|
51
|
+
const DECLARATION_LIST_CONTEXT = "declarationList";
|
|
52
|
+
const PARSE_CSS_ERROR_MESSAGE = "Failed to parse CSS";
|
|
53
|
+
const QSA_ERROR_MESSAGE = "Failed to match selector";
|
|
54
|
+
const ROOT_PSEUDO_CLASS = ":root";
|
|
55
|
+
const PRELUDE_SEPARATOR = ",";
|
|
56
|
+
const NESTING_SELECTOR = "&";
|
|
57
|
+
const VENDOR_PREFIX = "-";
|
|
58
|
+
const CUSTOM_PROPERTY_PREFIX = "--";
|
|
59
|
+
const LAYER_NAME_SEPARATOR = ".";
|
|
60
|
+
const DECLARATION_KEY_SEPARATOR = ":";
|
|
61
|
+
const CONTEXT_KEY_SEPARATOR = "|";
|
|
62
|
+
const BLOCK_OPEN = "{";
|
|
63
|
+
const BLOCK_CLOSE = "}";
|
|
64
|
+
const EMPTY_STRING = "";
|
|
65
|
+
const CSS_IMPORTANCE_NOT_IMPORTANT = 0;
|
|
66
|
+
const CSS_IMPORTANCE_IMPORTANT = 1;
|
|
67
|
+
|
|
28
68
|
export {
|
|
29
69
|
process
|
|
30
70
|
};
|
|
31
71
|
|
|
32
|
-
function process(
|
|
33
|
-
const
|
|
34
|
-
|
|
72
|
+
function process(doc, stylesheets) {
|
|
73
|
+
const docContext = {
|
|
74
|
+
doc,
|
|
75
|
+
stats: { processed: 0, discarded: 0 },
|
|
76
|
+
matchedElements: new Set(),
|
|
77
|
+
matchedSelectors: new Map(),
|
|
78
|
+
matchingSelectors: new Map(),
|
|
79
|
+
layerDeclarationCounter: 0,
|
|
80
|
+
layerDeclarations: [],
|
|
81
|
+
layerOrder: new Map(),
|
|
82
|
+
selectorData: new Map(),
|
|
83
|
+
selectorTexts: new Map(),
|
|
84
|
+
preludeTexts: new Map(),
|
|
85
|
+
scopeRoots: new Map(),
|
|
86
|
+
scopeSpecificities: new Map(),
|
|
87
|
+
rulesCounter: 0,
|
|
88
|
+
};
|
|
89
|
+
collectLayerOrder(stylesheets, docContext);
|
|
90
|
+
buildEffectiveLayerOrder(docContext);
|
|
91
|
+
minifyRules(stylesheets, docContext);
|
|
92
|
+
computeCascade(docContext);
|
|
93
|
+
removeEmptyRules(stylesheets, docContext);
|
|
94
|
+
return docContext.stats;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function collectLayerOrder(stylesheets, docContext) {
|
|
35
98
|
stylesheets.forEach((stylesheetInfo, key) => {
|
|
36
99
|
if (!stylesheetInfo.scoped && stylesheetInfo.stylesheet && !key.urlNode) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
100
|
+
if (hasChildNodes(stylesheetInfo.stylesheet)) {
|
|
101
|
+
collectStylesheetLayerOrder(stylesheetInfo.stylesheet.children, { layerStack: [], conditionalStack: [] }, docContext);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function buildEffectiveLayerOrder(docContext) {
|
|
108
|
+
const layerNames = [];
|
|
109
|
+
for (let indexDeclaration = 0; indexDeclaration < docContext.layerDeclarations.length; indexDeclaration++) {
|
|
110
|
+
const declaration = docContext.layerDeclarations[indexDeclaration];
|
|
111
|
+
layerNames.push(declaration.name);
|
|
112
|
+
}
|
|
113
|
+
for (let indexLayerName = 0; indexLayerName < layerNames.length; indexLayerName++) {
|
|
114
|
+
const name = layerNames[indexLayerName];
|
|
115
|
+
if (!docContext.layerOrder.has(name)) {
|
|
116
|
+
docContext.layerOrder.set(name, docContext.layerOrder.size);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function minifyRules(stylesheets, docContext) {
|
|
122
|
+
stylesheets.forEach((stylesheetInfo, key) => {
|
|
123
|
+
if (!stylesheetInfo.scoped && stylesheetInfo.stylesheet && !key.urlNode) {
|
|
124
|
+
if (hasChildNodes(stylesheetInfo.stylesheet)) {
|
|
125
|
+
const topConditionalStack = stylesheetInfo.mediaText ? [{ name: "media", prelude: stylesheetInfo.mediaText }] : [];
|
|
126
|
+
if (stylesheetInfo.layerName !== undefined) {
|
|
127
|
+
topConditionalStack.push({ name: "layer", prelude: stylesheetInfo.layerName });
|
|
46
128
|
}
|
|
47
|
-
|
|
48
|
-
|
|
129
|
+
if (stylesheetInfo.supportsCondition !== undefined) {
|
|
130
|
+
topConditionalStack.push({ name: "supports", prelude: stylesheetInfo.supportsCondition });
|
|
131
|
+
}
|
|
132
|
+
minifyStylesheetRules(stylesheetInfo.stylesheet.children, stylesheets, {
|
|
133
|
+
ancestorsSelectors: [],
|
|
134
|
+
layerStack: [],
|
|
135
|
+
conditionalStack: topConditionalStack
|
|
136
|
+
}, docContext);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function computeCascade(docContext) {
|
|
143
|
+
const winningDeclarations = new Set();
|
|
144
|
+
docContext.matchedElements.forEach(element => computeCascadedStylesForElement(element, winningDeclarations, docContext));
|
|
145
|
+
removeLosingDeclarations(winningDeclarations, docContext);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function removeEmptyRules(stylesheets, docContext) {
|
|
149
|
+
stylesheets.forEach((stylesheetInfo, key) => {
|
|
150
|
+
if (!stylesheetInfo.scoped && stylesheetInfo.stylesheet && !key.urlNode) {
|
|
151
|
+
if (hasChildNodes(stylesheetInfo.stylesheet)) {
|
|
152
|
+
removeStylesheetEmptyRules(stylesheetInfo.stylesheet.children, docContext);
|
|
49
153
|
}
|
|
50
154
|
}
|
|
51
|
-
sheetIndex++;
|
|
52
155
|
});
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function collectStylesheetLayerOrder(cssRules, layerContext, docContext) {
|
|
159
|
+
const { layerStack, conditionalStack } = layerContext;
|
|
160
|
+
for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
|
|
161
|
+
const ruleData = cssRule.data;
|
|
162
|
+
if (ruleData.type === AT_RULE_TYPE && ruleData.name === LAYER_NAME) {
|
|
163
|
+
collectStylesheetLayerRule(ruleData, layerStack, conditionalStack, docContext);
|
|
164
|
+
} else if (ruleData.type === AT_RULE_TYPE && hasChildNodes(ruleData.block)) {
|
|
165
|
+
const newConditionalStack = buildConditionalStack(conditionalStack, ruleData, docContext);
|
|
166
|
+
collectStylesheetLayerOrder(ruleData.block.children, { layerStack, conditionalStack: newConditionalStack }, docContext);
|
|
167
|
+
} else if (ruleData.type === RULE_TYPE && hasChildNodes(ruleData.block)) {
|
|
168
|
+
collectStylesheetLayerOrder(ruleData.block.children, layerContext, docContext);
|
|
169
|
+
}
|
|
57
170
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function collectStylesheetLayerRule(ruleData, layerStack, conditionalStack, docContext) {
|
|
174
|
+
if (ruleData.block) {
|
|
175
|
+
const layerName = getPreludeText(ruleData.prelude, docContext);
|
|
176
|
+
registerLayerDeclaration(layerStack, layerName, conditionalStack, docContext);
|
|
177
|
+
collectStylesheetLayerOrder(ruleData.block.children, { layerStack: [...layerStack, layerName], conditionalStack }, docContext);
|
|
178
|
+
} else if (ruleData.prelude) {
|
|
179
|
+
const layerNames = getPreludeText(ruleData.prelude, docContext).split(PRELUDE_SEPARATOR);
|
|
180
|
+
layerNames.forEach(layerName => registerLayerDeclaration(layerStack, layerName, conditionalStack, docContext));
|
|
61
181
|
}
|
|
62
|
-
return stats;
|
|
63
182
|
}
|
|
64
183
|
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
184
|
+
function buildConditionalStack(conditionalStack, ruleData, docContext) {
|
|
185
|
+
const isConditional = CONDITIONAL_AT_RULE_NAMES.has(ruleData.name);
|
|
186
|
+
return isConditional
|
|
187
|
+
? [...conditionalStack, { name: ruleData.name, prelude: getPreludeText(ruleData.prelude, docContext) }]
|
|
188
|
+
: conditionalStack;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function registerLayerDeclaration(layerStack, layerName, conditionalStack, docContext) {
|
|
192
|
+
const fullLayerName = getFullLayerName([...layerStack, layerName]);
|
|
193
|
+
if (fullLayerName) {
|
|
194
|
+
docContext.layerDeclarations.push({
|
|
195
|
+
name: fullLayerName,
|
|
196
|
+
order: docContext.layerDeclarationCounter++,
|
|
197
|
+
conditionalStack: conditionalStack.slice()
|
|
198
|
+
});
|
|
70
199
|
}
|
|
71
|
-
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function minifyStylesheetRules(cssRules, stylesheets, processingContext, docContext) {
|
|
203
|
+
const removedRules = new Set();
|
|
72
204
|
for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
205
|
+
docContext.stats.processed++;
|
|
206
|
+
minifyRule(cssRule.data, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
207
|
+
}
|
|
208
|
+
removedRules.forEach(cssRule => cssRules.remove(cssRule));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function minifyRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
|
|
212
|
+
if (ruleData.type === AT_RULE_TYPE && ruleData.name === IMPORT_NAME && hasChildNodes(ruleData.prelude) && ruleData.prelude.children.head.data.importedChildren) {
|
|
213
|
+
minifyImportRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
214
|
+
} else if (ruleData.type === AT_RULE_TYPE && ruleData.name === LAYER_NAME && hasChildNodes(ruleData.block)) {
|
|
215
|
+
minifyLayerRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
216
|
+
} else if (ruleData.type === AT_RULE_TYPE && ruleData.name === SCOPE_NAME && hasChildNodes(ruleData.block)) {
|
|
217
|
+
minifyScopeRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
218
|
+
} else if (ruleData.type === AT_RULE_TYPE && ruleData.name !== FONT_FACE_NAME && ruleData.name !== KEYFRAMES_NAME && !ruleData.name.startsWith(VENDOR_PREFIX) && hasChildNodes(ruleData.block)) {
|
|
219
|
+
minifyAtRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
220
|
+
} else if (ruleData.type === RULE_TYPE && hasChildNodes(ruleData.prelude)) {
|
|
221
|
+
minifyStylesheetRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function minifyImportRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
|
|
226
|
+
const urlNode = ruleData.prelude.children.head.data;
|
|
227
|
+
const topConditionalStack = urlNode.importedMediaText ? [{ name: "media", prelude: urlNode.importedMediaText }] : [];
|
|
228
|
+
if (urlNode.importedLayerName !== undefined) {
|
|
229
|
+
topConditionalStack.push({ name: "layer", prelude: urlNode.importedLayerName });
|
|
230
|
+
}
|
|
231
|
+
if (urlNode.importedSupportsCondition !== undefined) {
|
|
232
|
+
topConditionalStack.push({ name: "supports", prelude: urlNode.importedSupportsCondition });
|
|
233
|
+
}
|
|
234
|
+
minifyStylesheetRules(urlNode.importedChildren, stylesheets, {
|
|
235
|
+
...processingContext,
|
|
236
|
+
conditionalStack: topConditionalStack
|
|
237
|
+
}, docContext);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function minifyLayerRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
|
|
241
|
+
const layerName = getPreludeText(ruleData.prelude, docContext);
|
|
242
|
+
const newProcessingContext = { ...processingContext, layerStack: [...processingContext.layerStack, layerName] };
|
|
243
|
+
expandRawCssRules(ruleData);
|
|
244
|
+
minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
|
|
245
|
+
if (!hasChildNodes(ruleData.block)) {
|
|
246
|
+
docContext.stats.discarded++;
|
|
247
|
+
removedRules.add(cssRule);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function minifyScopeRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
|
|
252
|
+
const parsedPrelude = parsePrelude(ruleData.prelude);
|
|
253
|
+
const includeLists = parsedPrelude.include.map(item => item.text);
|
|
254
|
+
const excludeLists = parsedPrelude.exclude.map(item => item.text);
|
|
255
|
+
const newConditionalStack = buildConditionalStack(processingContext.conditionalStack, ruleData, docContext);
|
|
256
|
+
const newProcessingContext = {
|
|
257
|
+
...processingContext,
|
|
258
|
+
conditionalStack: newConditionalStack,
|
|
259
|
+
scopeIncludeLists: [...(processingContext.scopeIncludeLists || []), includeLists],
|
|
260
|
+
scopeExclusionLists: [...(processingContext.scopeExclusionLists || []), excludeLists],
|
|
261
|
+
scopeNestingLevel: (processingContext.scopeNestingLevel || 0) + 1
|
|
262
|
+
};
|
|
263
|
+
expandRawCssRules(ruleData);
|
|
264
|
+
minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
|
|
265
|
+
if (!hasChildNodes(ruleData.block)) {
|
|
266
|
+
docContext.stats.discarded++;
|
|
267
|
+
removedRules.add(cssRule);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function minifyAtRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
|
|
272
|
+
const newConditionalStack = buildConditionalStack(processingContext.conditionalStack, ruleData, docContext);
|
|
273
|
+
const newProcessingContext = { ...processingContext, conditionalStack: newConditionalStack };
|
|
274
|
+
expandRawCssRules(ruleData);
|
|
275
|
+
minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
|
|
276
|
+
if (!hasChildNodes(ruleData.block)) {
|
|
277
|
+
docContext.stats.discarded++;
|
|
278
|
+
removedRules.add(cssRule);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function minifyStylesheetRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
|
|
283
|
+
ruleData.order = docContext.rulesCounter++;
|
|
284
|
+
const removedSelectors = processSelectors(ruleData, processingContext, docContext);
|
|
285
|
+
const wasDiscarded = removeUnmatchedSelectors(ruleData, removedSelectors, removedRules, cssRule, docContext);
|
|
286
|
+
if (!wasDiscarded && hasChildNodes(ruleData.block)) {
|
|
287
|
+
processNestedRules(ruleData, stylesheets, processingContext, docContext);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function processSelectors(ruleData, processingContext, docContext) {
|
|
292
|
+
const removedSelectors = [];
|
|
293
|
+
const { ancestorsSelectors } = processingContext;
|
|
294
|
+
for (let selector = ruleData.prelude.children.head, selectorIndex = 0; selector; selector = selector.next, selectorIndex++) {
|
|
295
|
+
const {
|
|
296
|
+
startsWithCombinator,
|
|
297
|
+
hasPseudoElement,
|
|
298
|
+
hasDynamicStatePseudoClass,
|
|
299
|
+
scopeRelative
|
|
300
|
+
} = analyzeSelector(selector.data);
|
|
301
|
+
registerSelector(selector, ruleData, scopeRelative, processingContext, docContext);
|
|
302
|
+
if (!hasPseudoElement && !hasDynamicStatePseudoClass &&
|
|
303
|
+
(!startsWithCombinator || !ancestorsSelectors || !ancestorsSelectors.length)) {
|
|
304
|
+
const matchedElements = matchElements(selector, ancestorsSelectors, docContext);
|
|
305
|
+
if (matchedElements.length) {
|
|
306
|
+
updateMatchingSelectors(matchedElements, selector, docContext);
|
|
307
|
+
} else {
|
|
308
|
+
removedSelectors.push(selector);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return removedSelectors;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function analyzeSelector(selector) {
|
|
316
|
+
let hasPseudoElement = false;
|
|
317
|
+
let hasDynamicStatePseudoClass = false;
|
|
318
|
+
let hasNestingOrScope = false;
|
|
319
|
+
let startsWithCombinator = false;
|
|
320
|
+
cssTree.walk(selector, {
|
|
321
|
+
enter(node) {
|
|
322
|
+
if (node.type === PSEUDO_ELEMENT_SELECTOR_TYPE) {
|
|
323
|
+
hasPseudoElement = true;
|
|
324
|
+
} else if (node.type === PSEUDO_CLASS_SELECTOR_TYPE) {
|
|
325
|
+
if (CANONICAL_PSEUDO_ELEMENT_NAMES.has(node.name)) {
|
|
326
|
+
hasPseudoElement = true;
|
|
327
|
+
} else if (DYNAMIC_STATE_PSEUDO_CLASSES.has(node.name)) {
|
|
328
|
+
hasDynamicStatePseudoClass = true;
|
|
329
|
+
} else if (node.name === SCOPE_NAME) {
|
|
330
|
+
hasNestingOrScope = true;
|
|
331
|
+
}
|
|
332
|
+
} else if (node.type === NESTING_SELECTOR_TYPE) {
|
|
333
|
+
hasNestingOrScope = true;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
const firstChild = selector.children.head.data;
|
|
338
|
+
startsWithCombinator = firstChild && firstChild.type === COMBINATOR_NAME;
|
|
339
|
+
const scopeRelative = !startsWithCombinator && !hasNestingOrScope;
|
|
340
|
+
return { hasPseudoElement, hasDynamicStatePseudoClass, startsWithCombinator, scopeRelative };
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function updateMatchingSelectors(matchedElements, selector, docContext) {
|
|
344
|
+
matchedElements.forEach(element => {
|
|
345
|
+
docContext.matchedElements.add(element);
|
|
346
|
+
let matchingSelectors = docContext.matchingSelectors.get(element);
|
|
347
|
+
if (!matchingSelectors) {
|
|
348
|
+
matchingSelectors = [];
|
|
349
|
+
docContext.matchingSelectors.set(element, matchingSelectors);
|
|
350
|
+
element.matchingSelectors = matchingSelectors;
|
|
351
|
+
}
|
|
352
|
+
matchingSelectors.push(selector);
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function processNestedRules(ruleData, stylesheets, processingContext, docContext) {
|
|
357
|
+
expandRawCssRules(ruleData);
|
|
358
|
+
removeDuplicateDeclarations(ruleData.block);
|
|
359
|
+
const newProcessingContext = { ...processingContext, ancestorsSelectors: [...processingContext.ancestorsSelectors, ruleData.prelude] };
|
|
360
|
+
minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function registerSelector(selector, ruleData, scopeRelative, processingContext, docContext) {
|
|
364
|
+
const {
|
|
365
|
+
ancestorsSelectors,
|
|
366
|
+
layerStack,
|
|
367
|
+
conditionalStack,
|
|
368
|
+
scopeIncludeLists,
|
|
369
|
+
scopeExclusionLists,
|
|
370
|
+
scopeNestingLevel
|
|
371
|
+
} = processingContext;
|
|
372
|
+
docContext.selectorData.set(selector, {
|
|
373
|
+
specificity: computeMaxSpecificity(selector.data, ancestorsSelectors),
|
|
374
|
+
rule: ruleData,
|
|
375
|
+
layerStack,
|
|
376
|
+
conditionalStack,
|
|
377
|
+
scopeIncludeLists,
|
|
378
|
+
scopeExclusionLists,
|
|
379
|
+
scopeNestingLevel,
|
|
380
|
+
scopeRelative
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function computeCascadedStylesForElement(element, winningDeclarations, docContext) {
|
|
385
|
+
const cascadedStyles = new Map();
|
|
386
|
+
const allDeclarations = collectDeclarationItemsForElement(element, docContext);
|
|
387
|
+
const contextGroups = new Map();
|
|
388
|
+
allDeclarations.forEach(declarationData => {
|
|
389
|
+
const { selector } = declarationData;
|
|
390
|
+
const conditionalStack = getConditionalStackForSelector(selector, docContext);
|
|
391
|
+
const contextKey = createContextKey(conditionalStack);
|
|
392
|
+
if (!contextGroups.has(contextKey)) {
|
|
393
|
+
contextGroups.set(contextKey, []);
|
|
394
|
+
}
|
|
395
|
+
contextGroups.get(contextKey).push(declarationData);
|
|
396
|
+
});
|
|
397
|
+
contextGroups.forEach(declarations => {
|
|
398
|
+
declarations.sort((declarationA, declarationB) => compareDeclarations(declarationA, declarationB, docContext));
|
|
399
|
+
declarations.forEach(declarationData => {
|
|
400
|
+
const { selector, declaration } = declarationData;
|
|
401
|
+
const conditionalStack = getConditionalStackForSelector(selector, docContext);
|
|
402
|
+
cascadedStyles.set(declaration.data.property + DECLARATION_KEY_SEPARATOR + createContextKey(conditionalStack), {
|
|
403
|
+
selector,
|
|
404
|
+
declaration
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
cascadedStyles.forEach(({ declaration }) => winningDeclarations.add(declaration));
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function createContextKey(conditionalStack) {
|
|
412
|
+
return conditionalStack.map(context => `${context.name}:${context.prelude}`).join(CONTEXT_KEY_SEPARATOR);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function collectDeclarationItemsForElement(element, docContext) {
|
|
416
|
+
const matchingSelectors = docContext.matchingSelectors.get(element);
|
|
417
|
+
const allDeclarations = [];
|
|
418
|
+
matchingSelectors.forEach(selector => {
|
|
419
|
+
const cssRule = docContext.selectorData.get(selector).rule;
|
|
420
|
+
if (hasChildNodes(cssRule.block)) {
|
|
421
|
+
const declarations = cssRule.block.children;
|
|
422
|
+
for (let declaration = declarations.head; declaration; declaration = declaration.next) {
|
|
423
|
+
const { type, value } = declaration.data;
|
|
424
|
+
if (type === DECLARATION_TYPE && value && value.type !== RAW_TYPE) {
|
|
425
|
+
allDeclarations.push({
|
|
426
|
+
declaration,
|
|
427
|
+
selector,
|
|
428
|
+
effectiveSpecificity: computeEffectiveSpecificity(
|
|
429
|
+
docContext.selectorData.get(selector), element, docContext),
|
|
430
|
+
isInline: false
|
|
431
|
+
});
|
|
91
432
|
}
|
|
92
433
|
}
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
const inlineDeclarations = getInlineStyleDeclarations(element);
|
|
437
|
+
for (const declaration of inlineDeclarations) {
|
|
438
|
+
allDeclarations.push({
|
|
439
|
+
declaration: declaration.declaration,
|
|
440
|
+
effectiveSpecificity: declaration.effectiveSpecificity,
|
|
441
|
+
isInline: true
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
return allDeclarations;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function getConditionalStackForSelector(selector, docContext) {
|
|
448
|
+
let conditionalStack = [];
|
|
449
|
+
if (selector) {
|
|
450
|
+
const selectorData = docContext.selectorData.get(selector);
|
|
451
|
+
if (selectorData && selectorData.conditionalStack) {
|
|
452
|
+
conditionalStack = selectorData.conditionalStack;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return conditionalStack;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function matchElements(selector, ancestorsSelectors, docContext) {
|
|
459
|
+
const selectorText = createSelectorText(selector, ancestorsSelectors, docContext);
|
|
460
|
+
const selectorData = docContext.selectorData.get(selector);
|
|
461
|
+
const hasScope = selectorData && ((selectorData.scopeIncludeLists && selectorData.scopeIncludeLists.length) || selectorData.scopeNestingLevel > 0);
|
|
462
|
+
const cacheKey = createMatchCacheKey(hasScope, selectorData, selectorText);
|
|
463
|
+
const cached = docContext.matchedSelectors.get(cacheKey);
|
|
464
|
+
if (cached) {
|
|
465
|
+
return cached;
|
|
466
|
+
} else {
|
|
467
|
+
if (hasScope) {
|
|
468
|
+
return collectScopedMatches(cacheKey, selector, docContext);
|
|
93
469
|
} else {
|
|
94
|
-
|
|
95
|
-
|
|
470
|
+
const nodes = querySelectorAll(docContext.doc, selectorText, docContext.scopeRoots);
|
|
471
|
+
docContext.matchedSelectors.set(cacheKey, nodes);
|
|
472
|
+
return nodes;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function createSelectorText(selector, ancestorsSelectors, docContext) {
|
|
478
|
+
let selectorText;
|
|
479
|
+
if (ancestorsSelectors && ancestorsSelectors.length) {
|
|
480
|
+
selectorText = combineSelectorWithAncestors(selector.data, ancestorsSelectors, docContext);
|
|
481
|
+
const combinedAst = parseCss(selectorText, SELECTOR_LIST_CONTEXT);
|
|
482
|
+
selectorText = sanitizeSelector({ data: combinedAst }, ancestorsSelectors, docContext);
|
|
483
|
+
}
|
|
484
|
+
if (!selectorText) {
|
|
485
|
+
selectorText = sanitizeSelector(selector, ancestorsSelectors, docContext);
|
|
486
|
+
}
|
|
487
|
+
return selectorText;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function createMatchCacheKey(hasScope, selectorData, selectorText) {
|
|
491
|
+
if (hasScope) {
|
|
492
|
+
const include = selectorData.scopeIncludeLists || [];
|
|
493
|
+
const exclude = selectorData.scopeExclusionLists || [];
|
|
494
|
+
const relative = selectorData.scopeRelative ? 1 : 0;
|
|
495
|
+
const nesting = selectorData.scopeNestingLevel || 0;
|
|
496
|
+
return [
|
|
497
|
+
selectorText,
|
|
498
|
+
JSON.stringify(include),
|
|
499
|
+
JSON.stringify(exclude), String(relative), String(nesting)
|
|
500
|
+
].join(CONTEXT_KEY_SEPARATOR);
|
|
501
|
+
} else {
|
|
502
|
+
return selectorText;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function collectScopedMatches(cacheKey, selector, docContext) {
|
|
507
|
+
const selectorData = docContext.selectorData.get(selector);
|
|
508
|
+
const includeLists = selectorData.scopeIncludeLists && selectorData.scopeIncludeLists.length ? selectorData.scopeIncludeLists[selectorData.scopeIncludeLists.length - 1] : [];
|
|
509
|
+
const excludeLists = selectorData.scopeExclusionLists && selectorData.scopeExclusionLists.length ? selectorData.scopeExclusionLists[selectorData.scopeExclusionLists.length - 1] : [];
|
|
510
|
+
const matchedSet = new Set();
|
|
511
|
+
const includes = includeLists.length ? includeLists : [ROOT_PSEUDO_CLASS];
|
|
512
|
+
for (const includeSelector of includes) {
|
|
513
|
+
collectMatchesForInclude(includeSelector, selector, excludeLists, docContext, matchedSet);
|
|
514
|
+
}
|
|
515
|
+
const matchedElements = Array.from(matchedSet);
|
|
516
|
+
docContext.matchedSelectors.set(cacheKey, matchedElements);
|
|
517
|
+
return matchedElements;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function collectMatchesForInclude(includeSelector, selector, excludeLists, docContext, matchedSet) {
|
|
521
|
+
const rootsForInclude = getScopeRoots(includeSelector, docContext);
|
|
522
|
+
for (const rootForInclude of rootsForInclude) {
|
|
523
|
+
const roots = querySelectorForRoot(rootForInclude, normalizeForRoot(selector), docContext.scopeRoots);
|
|
524
|
+
if (roots.length) {
|
|
525
|
+
if (excludeLists && excludeLists.length) {
|
|
526
|
+
const filteredRoots = filterExcludedRoots(roots, excludeLists, docContext);
|
|
527
|
+
filteredRoots.forEach(root => matchedSet.add(root));
|
|
528
|
+
} else {
|
|
529
|
+
roots.forEach(root => matchedSet.add(root));
|
|
96
530
|
}
|
|
97
531
|
}
|
|
98
532
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function querySelectorForRoot(root, selector, cache) {
|
|
536
|
+
const nodes = querySelectorAll(root, selector, cache);
|
|
537
|
+
if (root.matches && root.matches(selector)) {
|
|
538
|
+
if (nodes.indexOf(root) === -1) {
|
|
539
|
+
nodes.unshift(root);
|
|
540
|
+
}
|
|
102
541
|
}
|
|
542
|
+
return nodes;
|
|
103
543
|
}
|
|
104
544
|
|
|
105
|
-
function
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
545
|
+
function normalizeForRoot(selector) {
|
|
546
|
+
const selectorData = cssTree.clone(selector.data);
|
|
547
|
+
cssTree.walk(selectorData, {
|
|
548
|
+
visit: NESTING_SELECTOR_TYPE,
|
|
549
|
+
enter(_node, item, list) {
|
|
550
|
+
const scope = { type: PSEUDO_CLASS_SELECTOR_TYPE, name: SCOPE_NAME };
|
|
551
|
+
list.insertData(scope, item);
|
|
552
|
+
list.remove(item);
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
for (let selectorChild = selectorData.children.head; selectorChild; selectorChild = selectorChild.next) {
|
|
556
|
+
const childData = selectorChild.data;
|
|
557
|
+
if (hasChildNodes(childData)) {
|
|
558
|
+
const head = childData.children.head;
|
|
559
|
+
const headData = head.data;
|
|
560
|
+
if (headData && headData.type === COMBINATOR_NAME) {
|
|
561
|
+
const scope = { type: PSEUDO_CLASS_SELECTOR_TYPE, name: SCOPE_NAME };
|
|
562
|
+
childData.children.insertData(scope, head);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return cssTree.generate(selectorData);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function getScopeRoots(selector, docContext) {
|
|
570
|
+
let roots = docContext.scopeRoots.get(selector);
|
|
571
|
+
if (!roots) {
|
|
572
|
+
roots = querySelectorAll(docContext.doc, selector, docContext.scopeRoots);
|
|
573
|
+
}
|
|
574
|
+
return roots;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function filterExcludedRoots(roots, excludeLists, docContext) {
|
|
578
|
+
const excludeRoots = new Set();
|
|
579
|
+
for (const excludeSelector of excludeLists) {
|
|
580
|
+
const rootsForExclude = getScopeRoots(excludeSelector, docContext);
|
|
581
|
+
rootsForExclude.forEach(root => excludeRoots.add(root));
|
|
582
|
+
}
|
|
583
|
+
return roots.filter(node => !Array.from(excludeRoots).some(excludedRoot => excludedRoot.contains(node)));
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function compareDeclarations(declarationA, declarationB, docContext) {
|
|
587
|
+
const importantA = declarationA.declaration.data.important ? CSS_IMPORTANCE_IMPORTANT : CSS_IMPORTANCE_NOT_IMPORTANT;
|
|
588
|
+
const importantB = declarationB.declaration.data.important ? CSS_IMPORTANCE_IMPORTANT : CSS_IMPORTANCE_NOT_IMPORTANT;
|
|
589
|
+
if (importantA !== importantB) {
|
|
590
|
+
return importantA - importantB;
|
|
591
|
+
}
|
|
592
|
+
if (declarationA.isInline && !declarationB.isInline) return 1;
|
|
593
|
+
if (!declarationA.isInline && declarationB.isInline) return -1;
|
|
594
|
+
let selectorDataA = declarationA.selector ? docContext.selectorData.get(declarationA.selector) : null;
|
|
595
|
+
let selectorDataB = declarationB.selector ? docContext.selectorData.get(declarationB.selector) : null;
|
|
596
|
+
if (selectorDataA && selectorDataB) {
|
|
597
|
+
const layerComparison = compareLayers(selectorDataA.layerStack, selectorDataB.layerStack, docContext);
|
|
598
|
+
if (layerComparison !== 0) {
|
|
599
|
+
return importantA ? -layerComparison : layerComparison;
|
|
600
|
+
}
|
|
601
|
+
const specificityA = declarationA.effectiveSpecificity;
|
|
602
|
+
const specificityB = declarationB.effectiveSpecificity;
|
|
603
|
+
if (specificityA.a !== specificityB.a) {
|
|
604
|
+
return specificityA.a - specificityB.a;
|
|
605
|
+
}
|
|
606
|
+
if (specificityA.b !== specificityB.b) {
|
|
607
|
+
return specificityA.b - specificityB.b;
|
|
113
608
|
}
|
|
114
|
-
if (
|
|
115
|
-
|
|
609
|
+
if (specificityA.c !== specificityB.c) {
|
|
610
|
+
return specificityA.c - specificityB.c;
|
|
611
|
+
}
|
|
612
|
+
if (selectorDataA.rule.order !== selectorDataB.rule.order) {
|
|
613
|
+
return selectorDataA.rule.order - selectorDataB.rule.order;
|
|
614
|
+
}
|
|
615
|
+
return 0;
|
|
616
|
+
} else {
|
|
617
|
+
const specificityA = declarationA.effectiveSpecificity;
|
|
618
|
+
const specificityB = declarationB.effectiveSpecificity;
|
|
619
|
+
if (specificityA.a !== specificityB.a) {
|
|
620
|
+
return specificityA.a - specificityB.a;
|
|
621
|
+
}
|
|
622
|
+
if (specificityA.b !== specificityB.b) {
|
|
623
|
+
return specificityA.b - specificityB.b;
|
|
624
|
+
}
|
|
625
|
+
if (specificityA.c !== specificityB.c) {
|
|
626
|
+
return specificityA.c - specificityB.c;
|
|
627
|
+
}
|
|
628
|
+
return 0;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function compareLayers(layersA, layersB, docContext) {
|
|
633
|
+
const isUnlayeredA = layersA.length === 0 || layersA.every(layerName => layerName === EMPTY_STRING);
|
|
634
|
+
const isUnlayeredB = layersB.length === 0 || layersB.every(layerName => layerName === EMPTY_STRING);
|
|
635
|
+
if (isUnlayeredA && isUnlayeredB) {
|
|
636
|
+
return 0;
|
|
637
|
+
}
|
|
638
|
+
if (isUnlayeredA) {
|
|
639
|
+
return 1;
|
|
640
|
+
}
|
|
641
|
+
if (isUnlayeredB) {
|
|
642
|
+
return -1;
|
|
643
|
+
}
|
|
644
|
+
const fullLayerNameA = getFullLayerName(layersA);
|
|
645
|
+
const fullLayerNameB = getFullLayerName(layersB);
|
|
646
|
+
if (fullLayerNameA === fullLayerNameB) {
|
|
647
|
+
return 0;
|
|
648
|
+
}
|
|
649
|
+
const minLength = Math.min(layersA.length, layersB.length);
|
|
650
|
+
const effectiveMap = docContext.layerOrder;
|
|
651
|
+
for (let indexLayer = 0; indexLayer < minLength; indexLayer++) {
|
|
652
|
+
if (layersA[indexLayer] !== layersB[indexLayer]) {
|
|
653
|
+
const partialLayerA = getFullLayerName(layersA.slice(0, indexLayer + 1));
|
|
654
|
+
const partialLayerB = getFullLayerName(layersB.slice(0, indexLayer + 1));
|
|
655
|
+
const orderA = effectiveMap.get(partialLayerA);
|
|
656
|
+
const orderB = effectiveMap.get(partialLayerB);
|
|
657
|
+
if (orderA !== undefined && orderB !== undefined) {
|
|
658
|
+
return orderA - orderB;
|
|
659
|
+
}
|
|
660
|
+
if (orderA !== undefined) {
|
|
661
|
+
return -1;
|
|
662
|
+
}
|
|
663
|
+
if (orderB !== undefined) {
|
|
664
|
+
return 1;
|
|
665
|
+
}
|
|
666
|
+
return 0;
|
|
116
667
|
}
|
|
117
668
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
669
|
+
return layersA.length - layersB.length;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function removeStylesheetEmptyRules(cssRules, docContext) {
|
|
673
|
+
const removedRules = new Set();
|
|
674
|
+
for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
|
|
675
|
+
const ruleData = cssRule.data;
|
|
676
|
+
if (ruleData.type === RULE_TYPE) {
|
|
677
|
+
if (hasChildNodes(ruleData.block)) {
|
|
678
|
+
removeStylesheetEmptyRules(ruleData.block.children, docContext);
|
|
679
|
+
} else {
|
|
680
|
+
docContext.stats.discarded++;
|
|
681
|
+
removedRules.add(cssRule);
|
|
682
|
+
}
|
|
683
|
+
} else if (ruleData.type === AT_RULE_TYPE && ruleData.block && ruleData.name !== FONT_FACE_NAME && ruleData.name !== KEYFRAMES_NAME) {
|
|
684
|
+
removeStylesheetEmptyRules(ruleData.block.children, docContext);
|
|
685
|
+
if (!hasChildNodes(ruleData.block)) {
|
|
686
|
+
docContext.stats.discarded++;
|
|
687
|
+
removedRules.add(cssRule);
|
|
122
688
|
}
|
|
123
689
|
}
|
|
124
690
|
}
|
|
125
|
-
|
|
126
|
-
|
|
691
|
+
removedRules.forEach(cssRule => cssRules.remove(cssRule));
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function removeUnmatchedSelectors(ruleData, removedSelectors, removedRules, cssRule, docContext) {
|
|
695
|
+
if (removedSelectors && removedSelectors.length) {
|
|
696
|
+
removedSelectors.forEach(selector => ruleData.prelude.children.remove(selector));
|
|
697
|
+
}
|
|
698
|
+
if (!hasChildNodes(ruleData.prelude)) {
|
|
699
|
+
docContext.stats.discarded++;
|
|
700
|
+
removedRules.add(cssRule);
|
|
701
|
+
return true;
|
|
702
|
+
}
|
|
703
|
+
return false;
|
|
127
704
|
}
|
|
128
705
|
|
|
129
|
-
function
|
|
706
|
+
function removeDuplicateDeclarations(block) {
|
|
707
|
+
const propertyMap = new Map();
|
|
130
708
|
const removedDeclarations = [];
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (
|
|
136
|
-
|
|
709
|
+
for (let ruleChildNode = block.children.head; ruleChildNode; ruleChildNode = ruleChildNode.next) {
|
|
710
|
+
if (ruleChildNode.data.type === DECLARATION_TYPE) {
|
|
711
|
+
const property = ruleChildNode.data.property;
|
|
712
|
+
const isImportant = ruleChildNode.data.important;
|
|
713
|
+
if (propertyMap.has(property)) {
|
|
714
|
+
const existing = propertyMap.get(property);
|
|
715
|
+
if (existing.isImportant === isImportant) {
|
|
716
|
+
removedDeclarations.push(existing.node);
|
|
717
|
+
propertyMap.set(property, { node: ruleChildNode, isImportant });
|
|
718
|
+
} else if (isImportant && !existing.isImportant) {
|
|
719
|
+
removedDeclarations.push(existing.node);
|
|
720
|
+
propertyMap.set(property, { node: ruleChildNode, isImportant });
|
|
721
|
+
} else {
|
|
722
|
+
removedDeclarations.push(ruleChildNode);
|
|
723
|
+
}
|
|
724
|
+
} else {
|
|
725
|
+
propertyMap.set(property, { node: ruleChildNode, isImportant });
|
|
137
726
|
}
|
|
138
727
|
}
|
|
139
|
-
removedDeclarations.forEach(declaration => styleData.children.remove(declaration));
|
|
140
728
|
}
|
|
729
|
+
removedDeclarations.forEach(declaration => block.children.remove(declaration));
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
function removeLosingDeclarations(winningDeclarations, docContext) {
|
|
733
|
+
const allDeclarations = new Map();
|
|
734
|
+
const protectedDeclarations = new Set();
|
|
735
|
+
docContext.matchedElements.forEach(element => {
|
|
736
|
+
const matchingSelectors = docContext.matchingSelectors.get(element);
|
|
737
|
+
if (matchingSelectors) {
|
|
738
|
+
matchingSelectors.forEach(selector => {
|
|
739
|
+
const cssRule = docContext.selectorData.get(selector).rule;
|
|
740
|
+
if (hasChildNodes(cssRule.block)) {
|
|
741
|
+
const declarations = cssRule.block.children;
|
|
742
|
+
for (let declaration = declarations.head; declaration; declaration = declaration.next) {
|
|
743
|
+
if (declaration.data.type === DECLARATION_TYPE) {
|
|
744
|
+
allDeclarations.set(declaration, declarations);
|
|
745
|
+
const { property, value } = declaration.data;
|
|
746
|
+
if (property && property.startsWith(CUSTOM_PROPERTY_PREFIX) || (value && value.type === RAW_TYPE)) {
|
|
747
|
+
protectedDeclarations.add(declaration);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
});
|
|
755
|
+
allDeclarations.forEach((list, node) => {
|
|
756
|
+
if (!winningDeclarations.has(node) && !protectedDeclarations.has(node)) {
|
|
757
|
+
list.remove(node);
|
|
758
|
+
}
|
|
759
|
+
});
|
|
141
760
|
}
|
|
142
761
|
|
|
143
|
-
function
|
|
144
|
-
|
|
762
|
+
function expandRawCssRules(ruleData) {
|
|
763
|
+
const ruleChildren = [];
|
|
764
|
+
if (hasChildNodes(ruleData.block)) {
|
|
765
|
+
for (let cssRuleNode = ruleData.block.children.head; cssRuleNode; cssRuleNode = cssRuleNode.next) {
|
|
766
|
+
if (cssRuleNode.data.type === RAW_TYPE) {
|
|
767
|
+
if (cssRuleNode.data.value.indexOf(BLOCK_OPEN) !== -1 &&
|
|
768
|
+
cssRuleNode.data.value.indexOf(BLOCK_OPEN) < cssRuleNode.data.value.indexOf(BLOCK_CLOSE)) {
|
|
769
|
+
try {
|
|
770
|
+
const stylesheet = parseCss(cssRuleNode.data.value, STYLESHEET_CONTEXT);
|
|
771
|
+
for (let stylesheetChild = stylesheet.children.head; stylesheetChild; stylesheetChild = stylesheetChild.next) {
|
|
772
|
+
ruleChildren.push(stylesheetChild);
|
|
773
|
+
}
|
|
774
|
+
} catch (error) {
|
|
775
|
+
if (DEBUG) {
|
|
776
|
+
// eslint-disable-next-line no-console
|
|
777
|
+
console.warn(PARSE_CSS_ERROR_MESSAGE, cssRuleNode.data.value, error);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
} else {
|
|
781
|
+
ruleChildren.push(cssRuleNode);
|
|
782
|
+
}
|
|
783
|
+
} else {
|
|
784
|
+
ruleChildren.push(cssRuleNode);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
ruleData.block.children.clear();
|
|
789
|
+
ruleChildren.forEach(ruleChild => ruleData.block.children.appendData(ruleChild.data));
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
function combineSelectorWithAncestors(selector, ancestorsSelectors, docContext) {
|
|
793
|
+
const selectorText = getSelectorText(selector, docContext);
|
|
794
|
+
if (!ancestorsSelectors || !ancestorsSelectors.length) {
|
|
795
|
+
return selectorText;
|
|
796
|
+
} else {
|
|
797
|
+
let contexts = [EMPTY_STRING];
|
|
798
|
+
ancestorsSelectors.forEach(selectorList => {
|
|
799
|
+
if (hasChildNodes(selectorList)) {
|
|
800
|
+
const parentSelectors = selectorList.children.toArray();
|
|
801
|
+
const nextContexts = [];
|
|
802
|
+
contexts.forEach(context => parentSelectors.forEach(parentSelector => {
|
|
803
|
+
const parentText = getSelectorText(parentSelector, docContext);
|
|
804
|
+
const combined = context ? combineSelectors(context, parentText) : parentText;
|
|
805
|
+
if (!nextContexts.includes(combined)) {
|
|
806
|
+
nextContexts.push(combined);
|
|
807
|
+
}
|
|
808
|
+
}));
|
|
809
|
+
if (nextContexts.length) {
|
|
810
|
+
contexts = nextContexts;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
const expandedSelectors = new Set();
|
|
815
|
+
contexts.forEach(context => {
|
|
816
|
+
const result = context ? combineSelectors(context, selectorText) : selectorText;
|
|
817
|
+
expandedSelectors.add(result);
|
|
818
|
+
});
|
|
819
|
+
return Array.from(expandedSelectors).join(PRELUDE_SEPARATOR);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function combineSelectors(parentSelectorText, childSelectorText) {
|
|
824
|
+
const childSelector = parseCss(childSelectorText || NESTING_SELECTOR);
|
|
825
|
+
const parentSelector = parentSelectorText ? parseCss(parentSelectorText) : null;
|
|
826
|
+
let hasNesting = false;
|
|
827
|
+
cssTree.walk(childSelector, {
|
|
828
|
+
visit: NESTING_SELECTOR_TYPE,
|
|
829
|
+
enter(_node, item, list) {
|
|
830
|
+
hasNesting = true;
|
|
831
|
+
if (!parentSelector) {
|
|
832
|
+
list.remove(item);
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
const nodes = parentSelector.children.toArray().map(parent => cssTree.clone(parent));
|
|
836
|
+
nodes.forEach(node => list.insertData(node, item));
|
|
837
|
+
list.remove(item);
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
if (hasNesting) {
|
|
841
|
+
return cssTree.generate(childSelector);
|
|
842
|
+
}
|
|
843
|
+
if (!parentSelector) {
|
|
844
|
+
return cssTree.generate(childSelector);
|
|
845
|
+
}
|
|
846
|
+
const combinedSelector = parseCss(`${parentSelectorText} ${childSelectorText}`);
|
|
847
|
+
return cssTree.generate(combinedSelector);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
function computeEffectiveSpecificity(selectorData, element, docContext) {
|
|
851
|
+
const baseSpecificity = selectorData.specificity;
|
|
852
|
+
let effectiveSpecificity = { a: baseSpecificity.a, b: baseSpecificity.b, c: baseSpecificity.c };
|
|
853
|
+
const includeLists = selectorData && selectorData.scopeIncludeLists && selectorData.scopeIncludeLists.length ? selectorData.scopeIncludeLists[selectorData.scopeIncludeLists.length - 1] : [];
|
|
854
|
+
if (includeLists && includeLists.length) {
|
|
855
|
+
for (const includeSelector of includeLists) {
|
|
856
|
+
const roots = getScopeRoots(includeSelector, docContext);
|
|
857
|
+
if (roots.some(root => root.contains(element))) {
|
|
858
|
+
const includeSpecificity = getIncludeSpecificity(includeSelector, docContext);
|
|
859
|
+
effectiveSpecificity = {
|
|
860
|
+
a: effectiveSpecificity.a + includeSpecificity.a,
|
|
861
|
+
b: effectiveSpecificity.b + includeSpecificity.b,
|
|
862
|
+
c: effectiveSpecificity.c + includeSpecificity.c
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return effectiveSpecificity;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
function getIncludeSpecificity(includeSelector, docContext) {
|
|
871
|
+
let specificity = docContext.scopeSpecificities.get(includeSelector);
|
|
872
|
+
if (!specificity) {
|
|
873
|
+
const selector = parseCss(includeSelector);
|
|
874
|
+
specificity = computeMaxSpecificity(selector, []);
|
|
875
|
+
docContext.scopeSpecificities.set(includeSelector, specificity);
|
|
876
|
+
}
|
|
877
|
+
return specificity;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
function hasChildNodes(node) {
|
|
881
|
+
return Boolean(node && node.children && node.children.head);
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
function getSelectorText(selector, docContext) {
|
|
885
|
+
if (!docContext.selectorTexts.has(selector)) {
|
|
886
|
+
docContext.selectorTexts.set(selector, cssTree.generate(selector));
|
|
887
|
+
}
|
|
888
|
+
return docContext.selectorTexts.get(selector);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function getPreludeText(prelude, docContext) {
|
|
892
|
+
if (prelude) {
|
|
893
|
+
if (!docContext.preludeTexts.has(prelude)) {
|
|
894
|
+
docContext.preludeTexts.set(prelude, cssTree.generate(prelude));
|
|
895
|
+
}
|
|
896
|
+
return docContext.preludeTexts.get(prelude);
|
|
897
|
+
} else {
|
|
898
|
+
return EMPTY_STRING;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
function getFullLayerName(layers) {
|
|
903
|
+
return layers.filter(layerName => layerName !== EMPTY_STRING).join(LAYER_NAME_SEPARATOR);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
function parseCss(text, context = SELECTOR_CONTEXT) {
|
|
907
|
+
const options = { context };
|
|
908
|
+
return cssTree.parse(text, options);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
function querySelectorAll(root, selector, cache) {
|
|
912
|
+
if (cache && cache !== root) {
|
|
913
|
+
let rootCache = cache.get(root);
|
|
914
|
+
if (!rootCache) {
|
|
915
|
+
rootCache = new Map();
|
|
916
|
+
cache.set(root, rootCache);
|
|
917
|
+
}
|
|
918
|
+
if (rootCache.has(selector)) {
|
|
919
|
+
return rootCache.get(selector);
|
|
920
|
+
} else {
|
|
921
|
+
try {
|
|
922
|
+
const nodes = Array.from(root.querySelectorAll(selector));
|
|
923
|
+
rootCache.set(selector, nodes);
|
|
924
|
+
return nodes;
|
|
925
|
+
} catch {
|
|
926
|
+
if (DEBUG) {
|
|
927
|
+
// eslint-disable-next-line no-console
|
|
928
|
+
console.warn(QSA_ERROR_MESSAGE, selector, root.tagName ? root.tagName : EMPTY_STRING);
|
|
929
|
+
}
|
|
930
|
+
rootCache.set(selector, []);
|
|
931
|
+
return [];
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
} else {
|
|
935
|
+
try {
|
|
936
|
+
return Array.from(root.querySelectorAll(selector));
|
|
937
|
+
} catch {
|
|
938
|
+
if (DEBUG) {
|
|
939
|
+
// eslint-disable-next-line no-console
|
|
940
|
+
console.warn(QSA_ERROR_MESSAGE, selector);
|
|
941
|
+
}
|
|
942
|
+
return [];
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
function getInlineStyleDeclarations(element) {
|
|
948
|
+
const style = element.getAttribute(STYLE_ATTRIBUTE_NAME);
|
|
949
|
+
if (style) {
|
|
950
|
+
let declarationNodes;
|
|
951
|
+
try {
|
|
952
|
+
declarationNodes = cssTree.parse(style, { context: DECLARATION_LIST_CONTEXT });
|
|
953
|
+
} catch {
|
|
954
|
+
return [];
|
|
955
|
+
}
|
|
956
|
+
const declarations = [];
|
|
957
|
+
for (let node = declarationNodes.children && declarationNodes.children.head; node; node = node.next) {
|
|
958
|
+
if (node.data.type === DECLARATION_TYPE) {
|
|
959
|
+
declarations.push({
|
|
960
|
+
declaration: node,
|
|
961
|
+
effectiveSpecificity: { a: 1, b: 0, c: 0 },
|
|
962
|
+
isInline: true
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
return declarations;
|
|
967
|
+
} else {
|
|
968
|
+
return [];
|
|
969
|
+
}
|
|
145
970
|
}
|