single-file-core 1.5.56 → 1.5.58
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 +105 -8
- package/core/index.js +8 -1
- package/core/lib/processor-helper-common.js +8 -28
- package/core/lib/processor-helper-inline.js +5 -0
- package/core/lib/processor-helper.js +6 -3
- package/core/util.js +6 -15
- package/modules/css-rules-minifier.js +3 -3
- package/package.json +1 -1
- package/processors/frame-tree/content/content-frame-tree.js +1 -1
- package/processors/hooks/content/content-hooks-frames-web.js +8 -4
package/core/helper.js
CHANGED
|
@@ -77,6 +77,7 @@ const EMPTY_RESOURCE = "data:,";
|
|
|
77
77
|
const DEFAULT_REPLACED_CHARACTERS = ["~", "+", "?", "%", "*", ":", "|", "\"", "<", ">", "\\\\", "\x00-\x1f", "\x7F"];
|
|
78
78
|
const DEFAULT_REPLACEMENT_CHARACTER = "_";
|
|
79
79
|
const DEFAULT_REPLACEMENT_CHARACTERS = ["~", "+", "?", "%", "*", ":", "|", """, "<", ">", "\"];
|
|
80
|
+
const NESTING_TRACK_ID_ATTRIBUTE_NAME = "data-sf-nesting-track-id";
|
|
80
81
|
const addEventListener = (type, listener, options) => globalThis.addEventListener(type, listener, options);
|
|
81
82
|
// eslint-disable-next-line no-unused-vars
|
|
82
83
|
const dispatchEvent = event => { try { globalThis.dispatchEvent(event); } catch (error) { /* ignored */ } };
|
|
@@ -87,6 +88,7 @@ const Blob = globalThis.Blob;
|
|
|
87
88
|
const CustomEvent = globalThis.CustomEvent;
|
|
88
89
|
const MutationObserver = globalThis.MutationObserver;
|
|
89
90
|
const URL = globalThis.URL;
|
|
91
|
+
const DOMParser = globalThis.DOMParser;
|
|
90
92
|
|
|
91
93
|
export {
|
|
92
94
|
initUserScriptHandler,
|
|
@@ -103,6 +105,8 @@ export {
|
|
|
103
105
|
getContentSize,
|
|
104
106
|
digest,
|
|
105
107
|
getValidFilename,
|
|
108
|
+
parseDocContent,
|
|
109
|
+
fixInvalidNesting,
|
|
106
110
|
ON_BEFORE_CAPTURE_EVENT_NAME,
|
|
107
111
|
ON_AFTER_CAPTURE_EVENT_NAME,
|
|
108
112
|
WIN_ID_ATTRIBUTE_NAME,
|
|
@@ -130,7 +134,8 @@ export {
|
|
|
130
134
|
INFOBAR_TAGNAME,
|
|
131
135
|
WAIT_FOR_USERSCRIPT_PROPERTY_NAME,
|
|
132
136
|
MESSAGE_PREFIX,
|
|
133
|
-
NO_SCRIPT_PROPERTY_NAME
|
|
137
|
+
NO_SCRIPT_PROPERTY_NAME,
|
|
138
|
+
NESTING_TRACK_ID_ATTRIBUTE_NAME
|
|
134
139
|
};
|
|
135
140
|
|
|
136
141
|
function initUserScriptHandler() {
|
|
@@ -205,13 +210,7 @@ function preProcessDoc(doc, win, options) {
|
|
|
205
210
|
const invalidElements = new Map();
|
|
206
211
|
let elementsInfo;
|
|
207
212
|
if (win && doc.documentElement) {
|
|
208
|
-
doc
|
|
209
|
-
const placeHolderElement = doc.createElement("template");
|
|
210
|
-
placeHolderElement.setAttribute(INVALID_ELEMENT_ATTRIBUTE_NAME, "");
|
|
211
|
-
placeHolderElement.content.appendChild(element.cloneNode(true));
|
|
212
|
-
invalidElements.set(element, placeHolderElement);
|
|
213
|
-
element.replaceWith(placeHolderElement);
|
|
214
|
-
});
|
|
213
|
+
markInvalidNesting(doc);
|
|
215
214
|
elementsInfo = getElementsInfo(win, doc, doc.documentElement, options);
|
|
216
215
|
if (options.moveStylesInHead) {
|
|
217
216
|
doc.querySelectorAll("body style, body ~ style").forEach(element => {
|
|
@@ -260,6 +259,87 @@ function preProcessDoc(doc, win, options) {
|
|
|
260
259
|
};
|
|
261
260
|
}
|
|
262
261
|
|
|
262
|
+
function markInvalidNesting(doc) {
|
|
263
|
+
addTrackIds(doc.body);
|
|
264
|
+
const verificationDoc = parseDocContent(serialize(doc));
|
|
265
|
+
const markedMap = buildTrackIdMap(doc.body);
|
|
266
|
+
const normalizedMap = buildTrackIdMap(verificationDoc.body);
|
|
267
|
+
const trackIds = new Set();
|
|
268
|
+
Object.keys(markedMap).forEach(id => {
|
|
269
|
+
if (id in normalizedMap) {
|
|
270
|
+
const markedParent = markedMap[id].parentElement?.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME) || null;
|
|
271
|
+
const normalizedParent = normalizedMap[id]?.parentElement?.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME) || null;
|
|
272
|
+
if (markedParent !== normalizedParent) {
|
|
273
|
+
let current = markedMap[id];
|
|
274
|
+
while (current && current !== doc.body) {
|
|
275
|
+
const currentId = current.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
276
|
+
if (currentId) {
|
|
277
|
+
trackIds.add(currentId);
|
|
278
|
+
}
|
|
279
|
+
current = current.parentElement;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
cleanupTrackIds(doc.body, trackIds);
|
|
285
|
+
|
|
286
|
+
function addTrackIds(element, index = 0, parentTrackId = "") {
|
|
287
|
+
const trackId = parentTrackId ? `${parentTrackId}.${index + 1}` : `${index + 1}`;
|
|
288
|
+
element.setAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME, trackId);
|
|
289
|
+
Array.from(element.children).forEach((child, indexChild) => addTrackIds(child, indexChild, trackId));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function buildTrackIdMap(element) {
|
|
293
|
+
const trackIds = {};
|
|
294
|
+
traverse(element);
|
|
295
|
+
return trackIds;
|
|
296
|
+
|
|
297
|
+
function traverse(element) {
|
|
298
|
+
if (element.getAttribute) {
|
|
299
|
+
const id = element.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
300
|
+
if (id) {
|
|
301
|
+
trackIds[id] = element;
|
|
302
|
+
}
|
|
303
|
+
Array.from(element.children).forEach(traverse);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function cleanupTrackIds(element, toKeep) {
|
|
309
|
+
const id = element.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
310
|
+
if (id && !toKeep.has(id)) {
|
|
311
|
+
element.removeAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
312
|
+
}
|
|
313
|
+
Array.from(element.children).forEach(child => cleanupTrackIds(child, toKeep));
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function fixInvalidNesting(document, NESTING_TRACK_ID_ATTRIBUTE_NAME) {
|
|
318
|
+
const trackIds = {};
|
|
319
|
+
document.currentScript.remove();
|
|
320
|
+
buildTrackIdMap(document.body);
|
|
321
|
+
Object.keys(trackIds).forEach(id => {
|
|
322
|
+
const element = trackIds[id];
|
|
323
|
+
const idParts = id.split(".");
|
|
324
|
+
if (idParts.length > 1) {
|
|
325
|
+
const parentId = idParts.slice(0, -1).join(".");
|
|
326
|
+
const expectedParent = trackIds[parentId];
|
|
327
|
+
if (expectedParent && element.parentElement !== expectedParent) {
|
|
328
|
+
expectedParent.appendChild(element);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
Object.keys(trackIds).forEach(id => trackIds[id].removeAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME));
|
|
333
|
+
|
|
334
|
+
function buildTrackIdMap(element) {
|
|
335
|
+
const id = element.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
336
|
+
if (id) {
|
|
337
|
+
trackIds[id] = element;
|
|
338
|
+
}
|
|
339
|
+
Array.from(element.children).forEach(buildTrackIdMap);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
263
343
|
function getElementsInfo(win, doc, element, options, data = { usedFonts: new Map(), canvases: [], images: [], posters: [], videos: [], shadowRoots: [], markedElements: [] }, adoptedStyleSheetsCache = new Map(), ascendantHidden) {
|
|
264
344
|
if (element.childNodes) {
|
|
265
345
|
const elements = Array.from(element.childNodes).filter(node => (node instanceof win.HTMLElement) || (node instanceof win.SVGElement) || (node instanceof globalThis.HTMLElement) || (node instanceof globalThis.SVGElement));
|
|
@@ -746,4 +826,21 @@ function getValidFilename(filename, replacedCharacters = DEFAULT_REPLACED_CHARAC
|
|
|
746
826
|
.replace(/\.\//g, "." + replacementCharacter)
|
|
747
827
|
.replace(/\/\./g, "/" + replacementCharacter);
|
|
748
828
|
return filename;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function parseDocContent(content, baseURI) {
|
|
832
|
+
const doc = (new DOMParser()).parseFromString(content, "text/html");
|
|
833
|
+
if (!doc.head) {
|
|
834
|
+
doc.documentElement.insertBefore(doc.createElement("HEAD"), doc.body);
|
|
835
|
+
}
|
|
836
|
+
let baseElement = doc.querySelector("base");
|
|
837
|
+
if (!baseElement || !baseElement.getAttribute("href")) {
|
|
838
|
+
if (baseElement) {
|
|
839
|
+
baseElement.remove();
|
|
840
|
+
}
|
|
841
|
+
baseElement = doc.createElement("base");
|
|
842
|
+
baseElement.setAttribute("href", baseURI);
|
|
843
|
+
doc.head.insertBefore(baseElement, doc.head.firstChild);
|
|
844
|
+
}
|
|
845
|
+
return doc;
|
|
749
846
|
}
|
package/core/index.js
CHANGED
|
@@ -582,6 +582,11 @@ class Processor {
|
|
|
582
582
|
if (this.options.displayStats) {
|
|
583
583
|
size = util.getContentSize(this.doc.documentElement.outerHTML);
|
|
584
584
|
}
|
|
585
|
+
if (this.doc.querySelector(`[${util.NESTING_TRACK_ID_ATTRIBUTE_NAME}]`)) {
|
|
586
|
+
const scriptElement = this.doc.createElement("script");
|
|
587
|
+
scriptElement.textContent = `(${util.getFixInvalidNestingSource()})(document, "${util.NESTING_TRACK_ID_ATTRIBUTE_NAME}");`;
|
|
588
|
+
this.doc.body.appendChild(scriptElement);
|
|
589
|
+
}
|
|
585
590
|
const content = util.serialize(this.doc, this.options.compressHTML);
|
|
586
591
|
if (this.options.displayStats) {
|
|
587
592
|
const contentSize = util.getContentSize(content);
|
|
@@ -893,8 +898,10 @@ class Processor {
|
|
|
893
898
|
if (!this.options.saveFavicon) {
|
|
894
899
|
this.doc.querySelectorAll("link[rel*=\"icon\"]").forEach(element => element.remove());
|
|
895
900
|
}
|
|
896
|
-
this.doc.querySelectorAll("a[ping]").forEach(element => element.removeAttribute("ping"));
|
|
901
|
+
this.doc.querySelectorAll("a[ping], area[ping]").forEach(element => element.removeAttribute("ping"));
|
|
902
|
+
this.doc.querySelectorAll("a[attributionsrc], img[attributionsrc], script[attributionsrc]").forEach(element => element.removeAttribute("attributionsrc"));
|
|
897
903
|
this.doc.querySelectorAll("link[rel=import][href]").forEach(element => element.remove());
|
|
904
|
+
this.doc.querySelectorAll("link[rel=compression-dictionary]").forEach(element => element.remove());
|
|
898
905
|
}
|
|
899
906
|
|
|
900
907
|
replaceInvalidElements() {
|
|
@@ -329,37 +329,17 @@ class ProcessorHelperCommon {
|
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
replacePseudoClassDefined(stylesheet) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
332
|
+
cssTree.walk(stylesheet, {
|
|
333
|
+
enter: function(node, item, list) {
|
|
334
|
+
if (node.type == "PseudoClassSelector" && node.name == "defined") {
|
|
335
|
+
if (item.prev == null || item.prev.data.type == "Combinator" || item.prev.data.type == "WhiteSpace") {
|
|
336
|
+
list.replace(item, cssTree.parse("*", { context: "selector" }).children.head);
|
|
337
|
+
} else {
|
|
338
|
+
list.remove(item);
|
|
339
339
|
}
|
|
340
340
|
}
|
|
341
341
|
}
|
|
342
|
-
}
|
|
343
|
-
if (removedSelectors.length) {
|
|
344
|
-
removedSelectors.forEach(({ parentSelector, selector }) => {
|
|
345
|
-
if (parentSelector.data.children.size == 0 || !selector.prev || selector.prev.data.type == "Combinator" || selector.prev.data.type == "WhiteSpace") {
|
|
346
|
-
parentSelector.data.children.replace(selector, cssTree.parse("*", { context: "selector" }).children.head);
|
|
347
|
-
} else {
|
|
348
|
-
parentSelector.data.children.remove(selector);
|
|
349
|
-
}
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
function replacePseudoDefinedSelector(selector, parentSelector) {
|
|
354
|
-
if (selector.data.children) {
|
|
355
|
-
for (let childSelector = selector.data.children.head; childSelector; childSelector = childSelector.next) {
|
|
356
|
-
replacePseudoDefinedSelector(childSelector, selector);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
if (selector.data.type == "PseudoClassSelector" && selector.data.name == "defined") {
|
|
360
|
-
removedSelectors.push({ parentSelector, selector });
|
|
361
|
-
}
|
|
362
|
-
}
|
|
342
|
+
});
|
|
363
343
|
}
|
|
364
344
|
|
|
365
345
|
resolveStylesheetURLs(stylesheet, baseURI, workStylesheet) {
|
|
@@ -99,6 +99,11 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
99
99
|
this.removeSingleLineCssComments(stylesheet);
|
|
100
100
|
}
|
|
101
101
|
this.replacePseudoClassDefined(stylesheet);
|
|
102
|
+
options.inlineStylesheets.forEach((content, index) => {
|
|
103
|
+
if (content === element.textContent) {
|
|
104
|
+
options.inlineStylesheets.set(index, this.generateStylesheetContent(stylesheet, options));
|
|
105
|
+
}
|
|
106
|
+
});
|
|
102
107
|
stylesheetInfo.stylesheet = stylesheet;
|
|
103
108
|
} else {
|
|
104
109
|
stylesheets.delete(element);
|
|
@@ -102,7 +102,10 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
102
102
|
linkElement.setAttribute("type", "text/css");
|
|
103
103
|
const name = "stylesheet_" + resources.stylesheets.size + ".css";
|
|
104
104
|
linkElement.setAttribute("href", name);
|
|
105
|
-
|
|
105
|
+
let content = options.inlineStylesheets.get(stylesheetRefIndex);
|
|
106
|
+
const stylesheet = cssTree.parse(content, { context: "stylesheet", parseCustomProperty: true });
|
|
107
|
+
this.replacePseudoClassDefined(stylesheet);
|
|
108
|
+
content = this.generateStylesheetContent(stylesheet, options);
|
|
106
109
|
resources.stylesheets.set(resources.stylesheets.size, { name, content });
|
|
107
110
|
linkElements.set(stylesheetRefIndex, linkElement);
|
|
108
111
|
});
|
|
@@ -247,9 +250,9 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
247
250
|
|
|
248
251
|
async processFrame(frameElement, pageData, options, resources, frameWindowId, frameData) {
|
|
249
252
|
const name = "frames/" + resources.frames.size + "/";
|
|
250
|
-
let sandbox = "allow-popups allow-top-navigation-by-user-activation";
|
|
253
|
+
let sandbox = "allow-popups allow-top-navigation-by-user-activation allow-scripts";
|
|
251
254
|
if (pageData.content.match(NOSCRIPT_TAG_FOUND) || pageData.content.match(CANVAS_TAG_FOUND) || pageData.content.match(SCRIPT_TAG_FOUND) || options.saveRawPage) {
|
|
252
|
-
sandbox += " allow-
|
|
255
|
+
sandbox += " allow-modals allow-popups allow-downloads allow-pointer-lock allow-presentation";
|
|
253
256
|
}
|
|
254
257
|
frameElement.setAttribute("sandbox", sandbox);
|
|
255
258
|
if (frameElement.tagName.toUpperCase() == "OBJECT") {
|
package/core/util.js
CHANGED
|
@@ -116,20 +116,7 @@ function getInstance(utilOptions) {
|
|
|
116
116
|
return helper.getValidFilename(filename, replacedCharacters, replacementCharacter, replacementCharacters);
|
|
117
117
|
},
|
|
118
118
|
parseDocContent(content, baseURI) {
|
|
119
|
-
|
|
120
|
-
if (!doc.head) {
|
|
121
|
-
doc.documentElement.insertBefore(doc.createElement("HEAD"), doc.body);
|
|
122
|
-
}
|
|
123
|
-
let baseElement = doc.querySelector("base");
|
|
124
|
-
if (!baseElement || !baseElement.getAttribute("href")) {
|
|
125
|
-
if (baseElement) {
|
|
126
|
-
baseElement.remove();
|
|
127
|
-
}
|
|
128
|
-
baseElement = doc.createElement("base");
|
|
129
|
-
baseElement.setAttribute("href", baseURI);
|
|
130
|
-
doc.head.insertBefore(baseElement, doc.head.firstChild);
|
|
131
|
-
}
|
|
132
|
-
return doc;
|
|
119
|
+
return helper.parseDocContent(content, baseURI);
|
|
133
120
|
},
|
|
134
121
|
parseXMLContent(content) {
|
|
135
122
|
return (new DOMParser()).parseFromString(content, "text/xml");
|
|
@@ -142,6 +129,9 @@ function getInstance(utilOptions) {
|
|
|
142
129
|
return doc;
|
|
143
130
|
}
|
|
144
131
|
},
|
|
132
|
+
getFixInvalidNestingSource() {
|
|
133
|
+
return helper.fixInvalidNesting.toString().replace(/\s+/g, " ");
|
|
134
|
+
},
|
|
145
135
|
async digest(algo, text) {
|
|
146
136
|
return helper.digest(algo, text);
|
|
147
137
|
},
|
|
@@ -229,7 +219,8 @@ function getInstance(utilOptions) {
|
|
|
229
219
|
EMPTY_RESOURCE: helper.EMPTY_RESOURCE,
|
|
230
220
|
INFOBAR_TAGNAME: helper.INFOBAR_TAGNAME,
|
|
231
221
|
WAIT_FOR_USERSCRIPT_PROPERTY_NAME: helper.WAIT_FOR_USERSCRIPT_PROPERTY_NAME,
|
|
232
|
-
NO_SCRIPT_PROPERTY_NAME: helper.NO_SCRIPT_PROPERTY_NAME
|
|
222
|
+
NO_SCRIPT_PROPERTY_NAME: helper.NO_SCRIPT_PROPERTY_NAME,
|
|
223
|
+
NESTING_TRACK_ID_ATTRIBUTE_NAME: helper.NESTING_TRACK_ID_ATTRIBUTE_NAME
|
|
233
224
|
};
|
|
234
225
|
|
|
235
226
|
async function getContent(resourceURL, options) {
|
|
@@ -217,7 +217,7 @@ function minifyRule(ruleData, cssRule, stylesheets, processingContext, removedRu
|
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
function minifyImportRule(ruleData,
|
|
220
|
+
function minifyImportRule(ruleData, _cssRule, stylesheets, processingContext, _removedRules, docContext) {
|
|
221
221
|
const urlNode = ruleData.prelude.children.head.data;
|
|
222
222
|
const topConditionalStack = urlNode.importedMediaText ? [{ name: "media", prelude: urlNode.importedMediaText }] : [];
|
|
223
223
|
if (urlNode.importedLayerName !== undefined) {
|
|
@@ -594,8 +594,8 @@ function compareDeclarations(declarationA, declarationB, docContext) {
|
|
|
594
594
|
}
|
|
595
595
|
if (declarationA.isInline && !declarationB.isInline) return 1;
|
|
596
596
|
if (!declarationA.isInline && declarationB.isInline) return -1;
|
|
597
|
-
|
|
598
|
-
|
|
597
|
+
const selectorDataA = declarationA.selector ? docContext.selectorData.get(declarationA.selector) : null;
|
|
598
|
+
const selectorDataB = declarationB.selector ? docContext.selectorData.get(declarationB.selector) : null;
|
|
599
599
|
if (selectorDataA && selectorDataB) {
|
|
600
600
|
const layerComparison = compareLayers(selectorDataA.layerStack, selectorDataB.layerStack, docContext);
|
|
601
601
|
if (layerComparison !== 0) {
|
package/package.json
CHANGED
|
@@ -439,7 +439,7 @@ function getFrames(document) {
|
|
|
439
439
|
document.querySelectorAll(ALL_ELEMENTS_CSS_SELECTOR).forEach(element => {
|
|
440
440
|
const shadowRoot = helper.getShadowRoot(element);
|
|
441
441
|
if (shadowRoot) {
|
|
442
|
-
frames = frames.concat(...shadowRoot
|
|
442
|
+
frames = frames.concat(...getFrames(shadowRoot));
|
|
443
443
|
}
|
|
444
444
|
});
|
|
445
445
|
return frames;
|
|
@@ -52,12 +52,16 @@
|
|
|
52
52
|
const BOOTSTRAP_EVENT = "single-file-bootstrap";
|
|
53
53
|
const FONT_STYLE_PROPERTIES = {
|
|
54
54
|
family: "font-family",
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
ascentOverride: "ascent-override",
|
|
56
|
+
descentOverride: "descent-override",
|
|
57
|
+
display: "font-display",
|
|
58
|
+
featureSettings: "font-feature-settings",
|
|
59
|
+
lineGapOverride: "line-gap-override",
|
|
57
60
|
stretch: "font-stretch",
|
|
61
|
+
style: "font-style",
|
|
58
62
|
unicodeRange: "unicode-range",
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
variationSettings: "font-variation-settings",
|
|
64
|
+
weight: "font-weight"
|
|
61
65
|
};
|
|
62
66
|
|
|
63
67
|
const fetch = globalThis.fetch.bind(globalThis);
|