single-file-core 1.6.5 → 1.6.6
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/helper.js +174 -37
- package/core/index.js +56 -32
- package/core/lib/processor-helper-common.js +26 -5
- package/core/lib/processor-helper-inline.js +115 -0
- package/core/lib/processor-helper.js +86 -60
- package/modules/css-fonts-minifier.js +139 -7
- package/modules/css-rules-minifier.js +32 -6
- package/modules/css-selector-sanitizer.js +12 -0
- package/package.json +1 -1
- package/processors/compression/compression-packager.js +8 -0
- package/processors/compression/compression.js +33 -11
- package/processors/frame-tree/content/content-frame-tree.js +2 -0
- package/processors/hooks/content/content-hooks-frames-web.js +3 -1
|
@@ -24,11 +24,12 @@
|
|
|
24
24
|
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
|
-
import { sanitizeSelector, matchUnqueryablePseudoClass } from "./css-selector-sanitizer.js";
|
|
27
|
+
import { sanitizeSelector, matchUnqueryableAttributeSelector, matchUnqueryablePseudoClass } from "./css-selector-sanitizer.js";
|
|
28
28
|
|
|
29
29
|
const DEBUG = false;
|
|
30
30
|
|
|
31
31
|
const PSEUDO_ELEMENT_SYNONYMS = new Set(["after", "before", "first-letter", "first-line"]);
|
|
32
|
+
const FUNCTIONAL_PSEUDO_CLASS_NAMES = new Set(["not", "is", "where", "has"]);
|
|
32
33
|
const MEDIA_AT_RULE_NAME = "media";
|
|
33
34
|
const SUPPORTS_AT_RULE_NAME = "supports";
|
|
34
35
|
const CONDITIONAL_AT_RULE_NAMES = new Set([MEDIA_AT_RULE_NAME, SUPPORTS_AT_RULE_NAME, "container"]);
|
|
@@ -36,6 +37,7 @@ const RULE_TYPE = "Rule";
|
|
|
36
37
|
const AT_RULE_TYPE = "Atrule";
|
|
37
38
|
const NESTING_SELECTOR_TYPE = "NestingSelector";
|
|
38
39
|
const PSEUDO_CLASS_SELECTOR_TYPE = "PseudoClassSelector";
|
|
40
|
+
const ATTRIBUTE_SELECTOR_TYPE = "AttributeSelector";
|
|
39
41
|
const DECLARATION_TYPE = "Declaration";
|
|
40
42
|
const RAW_TYPE = "Raw";
|
|
41
43
|
const VALUE_TYPE = "Value";
|
|
@@ -390,17 +392,20 @@ function processSelectors(ruleData, processingContext, docContext) {
|
|
|
390
392
|
for (let selector = ruleData.prelude.children.head, selectorIndex = 0; selector; selector = selector.next, selectorIndex++) {
|
|
391
393
|
const {
|
|
392
394
|
startsWithCombinator,
|
|
393
|
-
hasUnqueryableSelector
|
|
395
|
+
hasUnqueryableSelector,
|
|
396
|
+
hasNestedUnqueryablePseudoClass
|
|
394
397
|
} = analyzeSelector(selector.data);
|
|
395
398
|
if (hasUnqueryableSelector) {
|
|
396
399
|
ruleData.hasUnqueryableSelector = true;
|
|
397
400
|
}
|
|
398
401
|
registerSelector(selector, ruleData, processingContext, docContext);
|
|
399
|
-
if (!
|
|
402
|
+
if (!startsWithCombinator || !ancestorsSelectors || !ancestorsSelectors.length) {
|
|
400
403
|
const matchedElements = matchElements(selector, ancestorsSelectors, processingContext.scopeStack, docContext);
|
|
401
404
|
if (matchedElements.length) {
|
|
402
|
-
|
|
403
|
-
|
|
405
|
+
if (!hasUnqueryableSelector) {
|
|
406
|
+
updateMatchingSelectors(matchedElements, selector, docContext);
|
|
407
|
+
}
|
|
408
|
+
} else if (!hasNestedUnqueryablePseudoClass) {
|
|
404
409
|
removedSelectors.push(selector);
|
|
405
410
|
}
|
|
406
411
|
}
|
|
@@ -410,21 +415,42 @@ function processSelectors(ruleData, processingContext, docContext) {
|
|
|
410
415
|
|
|
411
416
|
function analyzeSelector(selector) {
|
|
412
417
|
let hasUnqueryableSelector = false;
|
|
418
|
+
let hasNestedUnqueryablePseudoClass = false;
|
|
413
419
|
let startsWithCombinator = false;
|
|
420
|
+
let functionalPseudoClassDepth = 0;
|
|
414
421
|
cssTree.walk(selector, {
|
|
415
422
|
enter(node) {
|
|
416
423
|
if (node.type === PSEUDO_ELEMENT_SELECTOR_TYPE) {
|
|
417
424
|
hasUnqueryableSelector = true;
|
|
425
|
+
if (functionalPseudoClassDepth) {
|
|
426
|
+
hasNestedUnqueryablePseudoClass = true;
|
|
427
|
+
}
|
|
418
428
|
} else if (node.type === PSEUDO_CLASS_SELECTOR_TYPE) {
|
|
419
429
|
if (PSEUDO_ELEMENT_SYNONYMS.has(node.name) || matchUnqueryablePseudoClass(node)) {
|
|
420
430
|
hasUnqueryableSelector = true;
|
|
431
|
+
if (functionalPseudoClassDepth) {
|
|
432
|
+
hasNestedUnqueryablePseudoClass = true;
|
|
433
|
+
}
|
|
421
434
|
}
|
|
435
|
+
if (FUNCTIONAL_PSEUDO_CLASS_NAMES.has(node.name.toLowerCase())) {
|
|
436
|
+
functionalPseudoClassDepth++;
|
|
437
|
+
}
|
|
438
|
+
} else if (node.type === ATTRIBUTE_SELECTOR_TYPE && matchUnqueryableAttributeSelector(node)) {
|
|
439
|
+
hasUnqueryableSelector = true;
|
|
440
|
+
if (functionalPseudoClassDepth) {
|
|
441
|
+
hasNestedUnqueryablePseudoClass = true;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
leave(node) {
|
|
446
|
+
if (node.type === PSEUDO_CLASS_SELECTOR_TYPE && FUNCTIONAL_PSEUDO_CLASS_NAMES.has(node.name.toLowerCase())) {
|
|
447
|
+
functionalPseudoClassDepth--;
|
|
422
448
|
}
|
|
423
449
|
}
|
|
424
450
|
});
|
|
425
451
|
const firstChild = selector.children.head.data;
|
|
426
452
|
startsWithCombinator = firstChild && firstChild.type === COMBINATOR_NAME;
|
|
427
|
-
return { hasUnqueryableSelector, startsWithCombinator };
|
|
453
|
+
return { hasUnqueryableSelector, hasNestedUnqueryablePseudoClass, startsWithCombinator };
|
|
428
454
|
}
|
|
429
455
|
|
|
430
456
|
function updateMatchingSelectors(matchedElements, selector, docContext) {
|
|
@@ -47,7 +47,11 @@ const FUNCTIONAL_PSEUDO_CLASSES = [
|
|
|
47
47
|
"where",
|
|
48
48
|
"has"
|
|
49
49
|
];
|
|
50
|
+
const STATE_ATTRIBUTE_NAMES = [
|
|
51
|
+
"open"
|
|
52
|
+
];
|
|
50
53
|
export {
|
|
54
|
+
matchUnqueryableAttributeSelector,
|
|
51
55
|
matchUnqueryablePseudoClass,
|
|
52
56
|
sanitizeSelector,
|
|
53
57
|
};
|
|
@@ -97,6 +101,10 @@ function normalizeSelectorNode(selector, ancestors) {
|
|
|
97
101
|
if (matchUnqueryablePseudoClass(childNode)) {
|
|
98
102
|
removeNode(selector.children, current);
|
|
99
103
|
}
|
|
104
|
+
} else if (childNode.type === "AttributeSelector") {
|
|
105
|
+
if (matchUnqueryableAttributeSelector(childNode)) {
|
|
106
|
+
removeNode(selector.children, current);
|
|
107
|
+
}
|
|
100
108
|
} else if (childNode.type === "Selector") {
|
|
101
109
|
normalizeSelectorNode(childNode, ancestors);
|
|
102
110
|
}
|
|
@@ -112,6 +120,10 @@ function removeNode(list, item) {
|
|
|
112
120
|
}
|
|
113
121
|
}
|
|
114
122
|
|
|
123
|
+
function matchUnqueryableAttributeSelector(attributeSelector) {
|
|
124
|
+
return Boolean(attributeSelector.name) && STATE_ATTRIBUTE_NAMES.includes(attributeSelector.name.name.toLowerCase());
|
|
125
|
+
}
|
|
126
|
+
|
|
115
127
|
function matchUnqueryablePseudoClass(pseudoClass) {
|
|
116
128
|
const name = pseudoClass.name.toLowerCase();
|
|
117
129
|
return pseudoClass.children ? (
|
package/package.json
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
import {
|
|
27
27
|
configure,
|
|
28
28
|
TextReader,
|
|
29
|
+
TextWriter,
|
|
29
30
|
Uint8ArrayReader,
|
|
30
31
|
Uint8ArrayWriter,
|
|
31
32
|
ZipReader
|
|
@@ -40,6 +41,7 @@ const browser = globalThis.browser;
|
|
|
40
41
|
|
|
41
42
|
const PAGES_PREFIX = "pages/";
|
|
42
43
|
const PAGES_FILENAME = "sfz-pages.json";
|
|
44
|
+
const MANIFEST_FILENAME = "manifest.json";
|
|
43
45
|
const TOC_FILENAME = "sfz-toc.html";
|
|
44
46
|
const TOC_TITLE = "Table of contents";
|
|
45
47
|
const TOC_STYLE = "body{font-family:system-ui,sans-serif;margin:2em auto;max-width:40em;padding:0 1em;background-color:#fff;color:#000}" +
|
|
@@ -102,6 +104,12 @@ async function createPagesArchive(pages, options) {
|
|
|
102
104
|
for (const entry of await zipReader.getEntries()) {
|
|
103
105
|
const filename = pagePath + entry.filename;
|
|
104
106
|
const rawData = entry.directory ? undefined : await entry.getData(new Uint8ArrayWriter(), { passThrough: true, checkCrc32: false });
|
|
107
|
+
if (entry.filename == MANIFEST_FILENAME && !entry.encrypted) {
|
|
108
|
+
const { archiveTime } = JSON.parse(await entry.getData(new TextWriter()));
|
|
109
|
+
if (archiveTime) {
|
|
110
|
+
manifest.pages[pageIndex].archiveTime = archiveTime;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
105
113
|
const canonicalFilename = writtenEntries && findDuplicate(writtenEntries, filename, entry, rawData);
|
|
106
114
|
if (canonicalFilename === undefined) {
|
|
107
115
|
await zipWriter.add(filename, entry.directory ? null : new Uint8ArrayReader(rawData), {
|
|
@@ -95,6 +95,7 @@ const MAX_ZIP_COMMENT_LENGTH = 65535;
|
|
|
95
95
|
const PDF_ENTRY_FILENAME = "page.pdf";
|
|
96
96
|
const PRESCAN_WINDOW_LENGTH = 1024;
|
|
97
97
|
const PNG_TEXT_CHUNK_HEADER_LENGTH = 12;
|
|
98
|
+
const PNG_LENGTH_PADDING_LENGTH = 1;
|
|
98
99
|
const PNG_ZIP_CHUNK_TYPE_KEYWORD = new Uint8Array([0x74, 0x45, 0x58, 0x74, 0x5a, 0x49, 0x50, 0]);
|
|
99
100
|
const MAX_HIDDEN_PNG_CHUNK_LENGTH = 0x2D000000;
|
|
100
101
|
const WRAPPER_PATTERN_WINDOW_LENGTH = 12;
|
|
@@ -277,7 +278,7 @@ async function buildArchive(pageData, options, script, entriesData, zipWriterOpt
|
|
|
277
278
|
const payloadView = new DataView(payload.buffer);
|
|
278
279
|
words.forEach((word, indexWord) => payloadView.setUint32(indexWord * 4, word, true));
|
|
279
280
|
extraData = "<sfz-extra-data>" + base64Encode(deflateRaw(payload)) + "</sfz-extra-data>";
|
|
280
|
-
if (options.preventAppendedData || extraData.length > getMaxAppendedDataLength(options) - pageContent.length - endTags.length - (options.embeddedImage ? PNG_IEND_LENGTH + PNG_CHUNK_CRC_LENGTH : 0)) {
|
|
281
|
+
if (options.preventAppendedData || extraData.length > getMaxAppendedDataLength(options) - pageContent.length - endTags.length - (options.embeddedImage ? PNG_IEND_LENGTH + PNG_CHUNK_CRC_LENGTH + PNG_LENGTH_PADDING_LENGTH : 0)) {
|
|
281
282
|
if (!options.extraDataSize) {
|
|
282
283
|
options.preventAppendedData = true;
|
|
283
284
|
options.extraDataSize = getReservationSize(extraData.length);
|
|
@@ -288,7 +289,11 @@ async function buildArchive(pageData, options, script, entriesData, zipWriterOpt
|
|
|
288
289
|
}
|
|
289
290
|
}
|
|
290
291
|
pageContent += endTags;
|
|
291
|
-
|
|
292
|
+
let pageContentData = new TextEncoder().encode(pageContent);
|
|
293
|
+
if (options.embeddedImage && !isChunkLengthHidden(data, embeddedImageDataOffset, zipDataWriter.offset + pageContentData.length - embeddedImageDataOffset - 4, imageChunk.tagIndex)) {
|
|
294
|
+
pageContentData = concatArrays(pageContentData, new Uint8Array(PNG_LENGTH_PADDING_LENGTH).fill(0x20));
|
|
295
|
+
}
|
|
296
|
+
await writeData(zipDataWriter.writable, pageContentData);
|
|
292
297
|
}
|
|
293
298
|
await zipDataWriter.writable.close();
|
|
294
299
|
const pageContent = await zipDataWriter.getData();
|
|
@@ -337,6 +342,15 @@ function isDeclaredLengthHidden(pageContent, zipDataEnd, appendedDataLength, opt
|
|
|
337
342
|
return !containsDataPattern(tail, EMBEDDED_DATA_PATTERNS[tagIndex]);
|
|
338
343
|
}
|
|
339
344
|
|
|
345
|
+
function isChunkLengthHidden(data, embeddedImageDataOffset, chunkLength, tagIndex) {
|
|
346
|
+
const lengthOffset = embeddedImageDataOffset - 4;
|
|
347
|
+
const window = concatArrays(
|
|
348
|
+
data.subarray(Math.max(0, lengthOffset - WRAPPER_PATTERN_WINDOW_LENGTH), lengthOffset),
|
|
349
|
+
getLength(chunkLength),
|
|
350
|
+
data.subarray(embeddedImageDataOffset, embeddedImageDataOffset + PNG_ZIP_CHUNK_TYPE_KEYWORD.length));
|
|
351
|
+
return !containsDataPattern(window, EMBEDDED_DATA_PATTERNS[tagIndex]);
|
|
352
|
+
}
|
|
353
|
+
|
|
340
354
|
function getCRC32(data, indexData = 0) {
|
|
341
355
|
const crcArray = new Uint8Array(4);
|
|
342
356
|
setUint32(crcArray, getCRC32Value(data, indexData));
|
|
@@ -723,7 +737,7 @@ function getImageHTMLChunk(pageData, options, lastModDate) {
|
|
|
723
737
|
findEmbeddedDataTagIndex(wrappedData, tagIndex) != tagIndex) {
|
|
724
738
|
tagIndex = findEmbeddedDataTagIndex(embeddedImageData, tagIndex + 1);
|
|
725
739
|
} else {
|
|
726
|
-
return { endTag, startHTMLData, htmlData, htmlDataCRC };
|
|
740
|
+
return { tagIndex, endTag, startHTMLData, htmlData, htmlDataCRC };
|
|
727
741
|
}
|
|
728
742
|
}
|
|
729
743
|
}
|
|
@@ -798,6 +812,7 @@ function isCompressibleContentType(contentType) {
|
|
|
798
812
|
async function getContent() {
|
|
799
813
|
const BASE64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
800
814
|
const DATA_IDENTIFIER = "sfz-data";
|
|
815
|
+
const WRAPPER_TAG_NAMES = ["script", "style", "noframes", "noembed", "iframe", "xmp", "svg", "plaintext"];
|
|
801
816
|
const { Blob, XMLHttpRequest, NodeFilter, document, zip, location } = globalThis;
|
|
802
817
|
const characterMap = new Map([
|
|
803
818
|
[65533, 0], [8364, 128], [8218, 130], [402, 131], [8222, 132], [8230, 133], [8224, 134], [8225, 135], [710, 136], [8240, 137],
|
|
@@ -902,15 +917,22 @@ async function getContent() {
|
|
|
902
917
|
if (zipDataElement) {
|
|
903
918
|
const inflatedPayload = zip.inflateRaw(base64Decode(zipDataElement.textContent));
|
|
904
919
|
const payload = new DataView(inflatedPayload.buffer, inflatedPayload.byteOffset, inflatedPayload.length & -4);
|
|
905
|
-
const
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
920
|
+
const candidates = Array.from(document.querySelectorAll("[id=" + DATA_IDENTIFIER + "]")).filter(element => WRAPPER_TAG_NAMES.includes(element.localName));
|
|
921
|
+
let startIndex = 0;
|
|
922
|
+
if (!candidates.length) {
|
|
923
|
+
const walker = document.createTreeWalker(document, NodeFilter.SHOW_COMMENT);
|
|
924
|
+
while (walker.nextNode()) {
|
|
925
|
+
if (walker.currentNode.data.startsWith(DATA_IDENTIFIER)) {
|
|
926
|
+
candidates.push(walker.currentNode);
|
|
927
|
+
}
|
|
913
928
|
}
|
|
929
|
+
startIndex = DATA_IDENTIFIER.length;
|
|
930
|
+
}
|
|
931
|
+
if (candidates.length > 1) {
|
|
932
|
+
throw new Error("Multiple zip data candidates found");
|
|
933
|
+
}
|
|
934
|
+
if (candidates.length) {
|
|
935
|
+
return decodeZipData(candidates[0], payload, startIndex);
|
|
914
936
|
}
|
|
915
937
|
}
|
|
916
938
|
throw new Error("Extra zip data not found");
|
|
@@ -231,6 +231,7 @@ function initResponse(message) {
|
|
|
231
231
|
frameData.posters = messageFrameData.posters;
|
|
232
232
|
frameData.videos = messageFrameData.videos;
|
|
233
233
|
frameData.usedFonts = messageFrameData.usedFonts;
|
|
234
|
+
frameData.usedFontsCharacters = messageFrameData.usedFontsCharacters;
|
|
234
235
|
frameData.shadowRoots = messageFrameData.shadowRoots;
|
|
235
236
|
frameData.processed = messageFrameData.processed;
|
|
236
237
|
frameData.scrollPosition = messageFrameData.scrollPosition;
|
|
@@ -453,6 +454,7 @@ function getFrameData(document, win, windowId, options, scrolling) {
|
|
|
453
454
|
posters: docData.posters,
|
|
454
455
|
videos: docData.videos,
|
|
455
456
|
usedFonts: docData.usedFonts,
|
|
457
|
+
usedFontsCharacters: docData.usedFontsCharacters,
|
|
456
458
|
shadowRoots: docData.shadowRoots,
|
|
457
459
|
scrollPosition: docData.scrollPosition,
|
|
458
460
|
scrolling,
|
|
@@ -586,7 +586,9 @@
|
|
|
586
586
|
if (adoptedStylesheetsData.has(stylesheet)) {
|
|
587
587
|
return adoptedStylesheetsData.get(stylesheet);
|
|
588
588
|
} else {
|
|
589
|
-
|
|
589
|
+
const text = Array.from(stylesheet.cssRules).map(cssRule => cssRule.cssText).join("\n");
|
|
590
|
+
adoptedStylesheetsData.set(stylesheet, text);
|
|
591
|
+
return text;
|
|
590
592
|
}
|
|
591
593
|
});
|
|
592
594
|
if (adoptedStyleSheets.length) {
|