single-file-core 1.6.25 → 1.6.27
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 +57 -2
- package/core/index.js +73 -3
- package/core/lib/processor-helper-common.js +2 -1
- package/core/lib/processor-helper-inline.js +4 -2
- package/core/lib/processor-helper.js +4 -2
- package/core/util.js +96 -5
- package/package.json +1 -1
- package/processors/frame-tree/content/content-frame-tree.js +2 -0
- package/single-file.js +9 -0
package/core/helper.js
CHANGED
|
@@ -376,10 +376,65 @@ function preProcessDoc(doc, win, options) {
|
|
|
376
376
|
markedElements: elementsInfo.markedElements,
|
|
377
377
|
invalidElements,
|
|
378
378
|
scrollPosition: { x: win.scrollX, y: win.scrollY },
|
|
379
|
-
adoptedStyleSheets: getStylesheetsContent(doc.adoptedStyleSheets)
|
|
379
|
+
adoptedStyleSheets: getStylesheetsContent(doc.adoptedStyleSheets),
|
|
380
|
+
manifestData: options.manifest ? getManifestData(doc, win) : undefined
|
|
380
381
|
};
|
|
381
382
|
}
|
|
382
383
|
|
|
384
|
+
function getManifestData(doc, win) {
|
|
385
|
+
const manifestData = { url: doc.documentURI, timings: {} };
|
|
386
|
+
try {
|
|
387
|
+
const lastModified = new Date(doc.lastModified);
|
|
388
|
+
if (!isNaN(lastModified)) {
|
|
389
|
+
manifestData.lastModified = lastModified.toISOString();
|
|
390
|
+
}
|
|
391
|
+
// eslint-disable-next-line no-unused-vars
|
|
392
|
+
} catch (error) {
|
|
393
|
+
// ignored
|
|
394
|
+
}
|
|
395
|
+
try {
|
|
396
|
+
const performance = win.performance;
|
|
397
|
+
manifestData.timeOrigin = new Date(performance.timeOrigin).toISOString();
|
|
398
|
+
const [navigation] = performance.getEntriesByType("navigation");
|
|
399
|
+
if (navigation) {
|
|
400
|
+
manifestData.navigation = getTimingData(navigation);
|
|
401
|
+
manifestData.navigation.type = navigation.type;
|
|
402
|
+
manifestData.navigation.redirectCount = navigation.redirectCount;
|
|
403
|
+
}
|
|
404
|
+
performance.getEntriesByType("resource").forEach(entry => manifestData.timings[entry.name] = getTimingData(entry));
|
|
405
|
+
// eslint-disable-next-line no-unused-vars
|
|
406
|
+
} catch (error) {
|
|
407
|
+
// ignored
|
|
408
|
+
}
|
|
409
|
+
try {
|
|
410
|
+
manifestData.environment = {
|
|
411
|
+
userAgent: win.navigator.userAgent,
|
|
412
|
+
viewport: { width: win.innerWidth, height: win.innerHeight, devicePixelRatio: win.devicePixelRatio },
|
|
413
|
+
media: {
|
|
414
|
+
prefersColorScheme: win.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light",
|
|
415
|
+
prefersReducedMotion: win.matchMedia("(prefers-reduced-motion: reduce)").matches ? "reduce" : "no-preference"
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
// eslint-disable-next-line no-unused-vars
|
|
419
|
+
} catch (error) {
|
|
420
|
+
// ignored
|
|
421
|
+
}
|
|
422
|
+
return manifestData;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function getTimingData(entry) {
|
|
426
|
+
const timingData = {
|
|
427
|
+
transferSize: entry.transferSize,
|
|
428
|
+
encodedBodySize: entry.encodedBodySize,
|
|
429
|
+
decodedBodySize: entry.decodedBodySize,
|
|
430
|
+
nextHopProtocol: entry.nextHopProtocol
|
|
431
|
+
};
|
|
432
|
+
if (entry.responseStatus) {
|
|
433
|
+
timingData.status = entry.responseStatus;
|
|
434
|
+
}
|
|
435
|
+
return timingData;
|
|
436
|
+
}
|
|
437
|
+
|
|
383
438
|
function insertCustomStylesheet(doc, content) {
|
|
384
439
|
const parentElement = doc.body || doc.documentElement;
|
|
385
440
|
if (parentElement) {
|
|
@@ -1351,7 +1406,7 @@ async function getDataURI(blob) {
|
|
|
1351
1406
|
|
|
1352
1407
|
async function digest(algo, text) {
|
|
1353
1408
|
try {
|
|
1354
|
-
const data = new TextEncoder("utf-8").encode(text);
|
|
1409
|
+
const data = typeof text == "string" ? new TextEncoder("utf-8").encode(text) : text instanceof Uint8Array ? text : new Uint8Array(text);
|
|
1355
1410
|
const hash = globalThis.crypto && crypto.subtle ? await crypto.subtle.digest(algo, data) : sha.digest(algo, data);
|
|
1356
1411
|
return hex(hash);
|
|
1357
1412
|
// eslint-disable-next-line no-unused-vars
|
package/core/index.js
CHANGED
|
@@ -207,6 +207,7 @@ class Runner {
|
|
|
207
207
|
this.options.shadowRoots = docData.shadowRoots;
|
|
208
208
|
this.options.referrer = docData.referrer;
|
|
209
209
|
this.options.adoptedStyleSheets = docData.adoptedStyleSheets;
|
|
210
|
+
this.options.manifestData = docData.manifestData;
|
|
210
211
|
this.markedElements = docData.markedElements;
|
|
211
212
|
this.invalidElements = docData.invalidElements;
|
|
212
213
|
}
|
|
@@ -403,8 +404,12 @@ class BatchRequest {
|
|
|
403
404
|
baseURI,
|
|
404
405
|
blockMixedContent,
|
|
405
406
|
acceptHeaders: options.acceptHeaders,
|
|
406
|
-
networkTimeout: options.networkTimeout
|
|
407
|
+
networkTimeout: options.networkTimeout,
|
|
408
|
+
manifestEntries: options.manifestEntries
|
|
407
409
|
});
|
|
410
|
+
if (content.manifestEntry) {
|
|
411
|
+
content.manifestEntry.references = resourceRequests.length;
|
|
412
|
+
}
|
|
408
413
|
if (!this.cancelled) {
|
|
409
414
|
const extension = util.getContentTypeExtension(content.contentType) || util.getFilenameExtension(resourceURL, options.filenameReplacedCharacters, options.filenameReplacementCharacter, options.filenameReplacementCharacters);
|
|
410
415
|
resourceRequests.forEach(callbacks => {
|
|
@@ -451,6 +456,10 @@ const UTF8_CHARSET = "utf-8";
|
|
|
451
456
|
const TAINTED_CANVAS_WARNING_MESSAGE = "SingleFile: canvas elements tainted by a cross-origin resource, dropped from the page:";
|
|
452
457
|
const EMPTY_RESOURCE = "data:,";
|
|
453
458
|
const ORDER_DEPENDENT_RULE = /@(layer|import)\b/i;
|
|
459
|
+
const MANIFEST_TOP_WINDOW_ID = "0";
|
|
460
|
+
const WINDOW_ID_SEPARATOR = ".";
|
|
461
|
+
const MANIFEST_EXCLUDED_OPTION_NAMES = ["rootDocument", "saveFilenameTemplateData", "omitReferrerInTemplateData", "manifest"];
|
|
462
|
+
const MANIFEST_STRING_OPTION_NAMES = ["customStylesheet", "filenameTemplate"];
|
|
454
463
|
|
|
455
464
|
class Processor {
|
|
456
465
|
constructor(options, processorHelper, batchRequest) {
|
|
@@ -471,7 +480,10 @@ class Processor {
|
|
|
471
480
|
frames: new Map()
|
|
472
481
|
};
|
|
473
482
|
this.fontTests = options.fontTests;
|
|
483
|
+
this.frameManifests = [];
|
|
484
|
+
this.startDate = new Date();
|
|
474
485
|
options.generatedDataURIs = new Set();
|
|
486
|
+
options.manifestEntries = options.manifest ? [] : undefined;
|
|
475
487
|
}
|
|
476
488
|
|
|
477
489
|
registerGeneratedDataURI(dataURI) {
|
|
@@ -504,7 +516,8 @@ class Processor {
|
|
|
504
516
|
resourceReferrer: this.options.resourceReferrer,
|
|
505
517
|
expectedType: "document",
|
|
506
518
|
acceptHeaders: this.options.acceptHeaders,
|
|
507
|
-
networkTimeout: this.options.networkTimeout
|
|
519
|
+
networkTimeout: this.options.networkTimeout,
|
|
520
|
+
manifestEntries: this.options.manifestEntries
|
|
508
521
|
});
|
|
509
522
|
pageContent = content.data || "";
|
|
510
523
|
}
|
|
@@ -654,12 +667,52 @@ class Processor {
|
|
|
654
667
|
if (this.options.addProof) {
|
|
655
668
|
pageData.hash = await util.digest("SHA-256", content);
|
|
656
669
|
}
|
|
670
|
+
if (this.options.manifest) {
|
|
671
|
+
pageData.manifest = this.getManifest();
|
|
672
|
+
}
|
|
657
673
|
if (this.options.retrieveLinks) {
|
|
658
674
|
pageData.links = Array.from(new Set(Array.from(this.doc.links).map(linkElement => linkElement.href)));
|
|
659
675
|
}
|
|
660
676
|
return pageData;
|
|
661
677
|
}
|
|
662
678
|
|
|
679
|
+
getManifest() {
|
|
680
|
+
const windowId = this.options.windowId || MANIFEST_TOP_WINDOW_ID;
|
|
681
|
+
const manifestData = this.options.manifestData || {};
|
|
682
|
+
const frame = {
|
|
683
|
+
id: windowId,
|
|
684
|
+
parent: windowId.includes(WINDOW_ID_SEPARATOR) ? windowId.substring(0, windowId.lastIndexOf(WINDOW_ID_SEPARATOR)) : null,
|
|
685
|
+
url: this.baseURI
|
|
686
|
+
};
|
|
687
|
+
if (manifestData.lastModified) {
|
|
688
|
+
frame.lastModified = manifestData.lastModified;
|
|
689
|
+
}
|
|
690
|
+
if (manifestData.navigation) {
|
|
691
|
+
frame.navigation = manifestData.navigation;
|
|
692
|
+
}
|
|
693
|
+
frame.resources = this.options.manifestEntries.map(entry => {
|
|
694
|
+
const timing = manifestData.timings && (manifestData.timings[entry.finalUrl] || manifestData.timings[entry.url]);
|
|
695
|
+
return timing ? Object.assign({}, entry, { timing }) : entry;
|
|
696
|
+
});
|
|
697
|
+
const frames = [frame, ...this.frameManifests];
|
|
698
|
+
if (!this.options.rootDocument) {
|
|
699
|
+
return { frames };
|
|
700
|
+
}
|
|
701
|
+
const manifest = {
|
|
702
|
+
subject: { url: this.options.saveUrl, finalUrl: manifestData.url || this.baseURI, title: this.doc.title },
|
|
703
|
+
time: { captureStarted: this.startDate.toISOString(), captureEnded: new Date().toISOString() }
|
|
704
|
+
};
|
|
705
|
+
if (manifestData.timeOrigin) {
|
|
706
|
+
manifest.time.loaded = manifestData.timeOrigin;
|
|
707
|
+
}
|
|
708
|
+
if (manifestData.environment) {
|
|
709
|
+
manifest.environment = manifestData.environment;
|
|
710
|
+
}
|
|
711
|
+
manifest.options = getManifestOptions(this.options);
|
|
712
|
+
manifest.frames = frames;
|
|
713
|
+
return manifest;
|
|
714
|
+
}
|
|
715
|
+
|
|
663
716
|
preProcessPage() {
|
|
664
717
|
this.doc.querySelectorAll("style[" + util.CUSTOM_STYLESHEET_ATTRIBUTE_NAME + "]").forEach(element => element.removeAttribute(util.CUSTOM_STYLESHEET_ATTRIBUTE_NAME));
|
|
665
718
|
this.doc.body.querySelectorAll(":not(svg) title, meta, link[href][rel*=\"icon\"]").forEach(element => {
|
|
@@ -1463,6 +1516,7 @@ class Processor {
|
|
|
1463
1516
|
options.scrollPosition = frameData.scrollPosition;
|
|
1464
1517
|
options.scrolling = frameData.scrolling;
|
|
1465
1518
|
options.adoptedStyleSheets = frameData.adoptedStyleSheets;
|
|
1519
|
+
options.manifestData = frameData.manifestData;
|
|
1466
1520
|
frameData.runner = new Runner(options, processorHelper);
|
|
1467
1521
|
frameData.frameElement = frameElement;
|
|
1468
1522
|
await frameData.runner.loadPage();
|
|
@@ -1668,6 +1722,9 @@ class Processor {
|
|
|
1668
1722
|
const { frameElement, pageData, frameWindowId, frameData } = capturedFrame;
|
|
1669
1723
|
this.processorHelper.processFrame(frameElement, pageData, this.options, this.resources, frameWindowId, frameData);
|
|
1670
1724
|
this.stats.addAll(pageData);
|
|
1725
|
+
if (pageData.manifest) {
|
|
1726
|
+
this.frameManifests.push(...pageData.manifest.frames);
|
|
1727
|
+
}
|
|
1671
1728
|
}
|
|
1672
1729
|
});
|
|
1673
1730
|
}
|
|
@@ -1702,7 +1759,8 @@ class Processor {
|
|
|
1702
1759
|
this.doc.head.appendChild(styleElement);
|
|
1703
1760
|
}
|
|
1704
1761
|
let stylesheetContent = "";
|
|
1705
|
-
cssVariables.
|
|
1762
|
+
const variables = Array.from(cssVariables.entries()).sort(([firstIndex], [secondIndex]) => firstIndex - secondIndex);
|
|
1763
|
+
variables.forEach(([indexResource, { content, url }]) => {
|
|
1706
1764
|
cssVariables.delete(indexResource);
|
|
1707
1765
|
if (stylesheetContent) {
|
|
1708
1766
|
stylesheetContent += ";";
|
|
@@ -1911,6 +1969,18 @@ function nameAssignedElements(hostElement) {
|
|
|
1911
1969
|
return splitSlots;
|
|
1912
1970
|
}
|
|
1913
1971
|
|
|
1972
|
+
function getManifestOptions(options) {
|
|
1973
|
+
const manifestOptions = {};
|
|
1974
|
+
Object.keys(options).sort().forEach(name => {
|
|
1975
|
+
const value = options[name];
|
|
1976
|
+
const primitive = typeof value == "boolean" || typeof value == "number";
|
|
1977
|
+
if ((primitive && !MANIFEST_EXCLUDED_OPTION_NAMES.includes(name)) || (typeof value == "string" && MANIFEST_STRING_OPTION_NAMES.includes(name))) {
|
|
1978
|
+
manifestOptions[name] = value;
|
|
1979
|
+
}
|
|
1980
|
+
});
|
|
1981
|
+
return manifestOptions;
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1914
1984
|
function removeInsertedParagraphs(doc) {
|
|
1915
1985
|
getInsertedParagraphs(doc).forEach((_, paragraph) => paragraph.remove());
|
|
1916
1986
|
}
|
|
@@ -312,7 +312,8 @@ class ProcessorHelperCommon {
|
|
|
312
312
|
blockMixedContent: options.blockMixedContent,
|
|
313
313
|
expectedType: "stylesheet",
|
|
314
314
|
acceptHeaders: options.acceptHeaders,
|
|
315
|
-
networkTimeout: options.networkTimeout
|
|
315
|
+
networkTimeout: options.networkTimeout,
|
|
316
|
+
manifestEntries: options.manifestEntries
|
|
316
317
|
});
|
|
317
318
|
if (!(matchCharsetEquals(content.data, content.charset) || matchCharsetEquals(content.data, options.charset))) {
|
|
318
319
|
options = Object.assign({}, options, { charset: getCharset(content.data) });
|
|
@@ -263,7 +263,8 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
263
263
|
blockMixedContent: options.blockMixedContent,
|
|
264
264
|
expectedType: "stylesheet",
|
|
265
265
|
acceptHeaders: options.acceptHeaders,
|
|
266
|
-
networkTimeout: options.networkTimeout
|
|
266
|
+
networkTimeout: options.networkTimeout,
|
|
267
|
+
manifestEntries: options.manifestEntries
|
|
267
268
|
});
|
|
268
269
|
if (content.failed) {
|
|
269
270
|
const liveContent = this.getLiveStylesheetContent(element, options);
|
|
@@ -405,7 +406,8 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
405
406
|
resourceReferrer: options.resourceReferrer,
|
|
406
407
|
referrerPolicy: getReferrerPolicy(resourceElement),
|
|
407
408
|
acceptHeaders: options.acceptHeaders,
|
|
408
|
-
networkTimeout: options.networkTimeout
|
|
409
|
+
networkTimeout: options.networkTimeout,
|
|
410
|
+
manifestEntries: options.manifestEntries
|
|
409
411
|
})).data;
|
|
410
412
|
// eslint-disable-next-line no-unused-vars
|
|
411
413
|
} catch (error) {
|
|
@@ -288,7 +288,8 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
288
288
|
blockMixedContent: options.blockMixedContent,
|
|
289
289
|
expectedType: "stylesheet",
|
|
290
290
|
acceptHeaders: options.acceptHeaders,
|
|
291
|
-
networkTimeout: options.networkTimeout
|
|
291
|
+
networkTimeout: options.networkTimeout,
|
|
292
|
+
manifestEntries: options.manifestEntries
|
|
292
293
|
});
|
|
293
294
|
if (content.failed) {
|
|
294
295
|
const liveContent = this.getLiveStylesheetContent(element, options);
|
|
@@ -477,7 +478,8 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
477
478
|
resourceReferrer: options.resourceReferrer,
|
|
478
479
|
referrerPolicy: getReferrerPolicy(resourceElement),
|
|
479
480
|
acceptHeaders: options.acceptHeaders,
|
|
480
|
-
networkTimeout: options.networkTimeout
|
|
481
|
+
networkTimeout: options.networkTimeout,
|
|
482
|
+
manifestEntries: options.manifestEntries
|
|
481
483
|
})).data;
|
|
482
484
|
// eslint-disable-next-line no-unused-vars
|
|
483
485
|
} catch (error) {
|
package/core/util.js
CHANGED
|
@@ -72,6 +72,15 @@ const CONTENT_TYPES_HTML = ["text/html", "application/xhtml+xml"];
|
|
|
72
72
|
const EXPECTED_TYPES_MEDIA = ["font", "image", "video", "audio", "model"];
|
|
73
73
|
const EXPECTED_TYPE_IMAGE = "image";
|
|
74
74
|
const EXPECTED_TYPE_DOCUMENT = "document";
|
|
75
|
+
const DATA_URI_PREFIX = "data:";
|
|
76
|
+
const MANIFEST_OUTCOME_EMBEDDED = "embedded";
|
|
77
|
+
const MANIFEST_OUTCOME_FAILED = "failed";
|
|
78
|
+
const MANIFEST_OUTCOME_BLOCKED = "blocked";
|
|
79
|
+
const MANIFEST_OUTCOME_REJECTED = "rejected";
|
|
80
|
+
const MANIFEST_OUTCOME_OVERSIZED = "oversized";
|
|
81
|
+
const MANIFEST_FETCHED_BY_PAGE = "page";
|
|
82
|
+
const MANIFEST_FETCHED_BY_FRAME = "frame";
|
|
83
|
+
const MANIFEST_HEADER_NAMES = ["date", "last-modified", "etag", "content-length", "content-encoding", "cache-control", "age", "expires", "server"];
|
|
75
84
|
|
|
76
85
|
const URL = globalThis.URL;
|
|
77
86
|
const DOMParser = globalThis.DOMParser;
|
|
@@ -246,6 +255,16 @@ function getInstance(utilOptions) {
|
|
|
246
255
|
};
|
|
247
256
|
|
|
248
257
|
async function getContent(resourceURL, options) {
|
|
258
|
+
const entry = options.manifestEntries && !resourceURL.startsWith(DATA_URI_PREFIX) ? { url: resourceURL, role: options.expectedType || EXPECTED_TYPE_DOCUMENT, outcome: MANIFEST_OUTCOME_FAILED } : undefined;
|
|
259
|
+
const content = await fetchContent(resourceURL, options, entry);
|
|
260
|
+
if (entry) {
|
|
261
|
+
options.manifestEntries.push(entry);
|
|
262
|
+
content.manifestEntry = entry;
|
|
263
|
+
}
|
|
264
|
+
return content;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async function fetchContent(resourceURL, options, entry) {
|
|
249
268
|
let response, startTime, networkTimeoutId, networkTimeoutPromise, resolveNetworkTimeoutPromise;
|
|
250
269
|
const fetchResource = utilOptions.fetch;
|
|
251
270
|
const fetchFrameResource = utilOptions.frameFetch;
|
|
@@ -254,6 +273,7 @@ function getInstance(utilOptions) {
|
|
|
254
273
|
log(" // STARTED download url =", resourceURL, "asBinary =", options.asBinary);
|
|
255
274
|
}
|
|
256
275
|
if (options.blockMixedContent && /^https:/i.test(options.baseURI) && !/^https:/i.test(resourceURL) && !/^blob:https:/i.test(resourceURL)) {
|
|
276
|
+
setManifestOutcome(entry, MANIFEST_OUTCOME_BLOCKED);
|
|
257
277
|
return getFetchResponse(resourceURL, options);
|
|
258
278
|
}
|
|
259
279
|
if (options.networkTimeout) {
|
|
@@ -274,21 +294,24 @@ function getInstance(utilOptions) {
|
|
|
274
294
|
fetchFrameResource(resourceURL, { frameId: options.frameId, referrer: options.resourceReferrer, referrerPolicy: options.referrerPolicy || undefined, stylesheetURL: options.stylesheetURL || undefined, stylesheetReferrerPolicy: options.stylesheetReferrerPolicy || undefined, headers: { accept } }),
|
|
275
295
|
networkTimeoutPromise
|
|
276
296
|
]);
|
|
297
|
+
setManifestResponse(entry, response, MANIFEST_FETCHED_BY_FRAME);
|
|
277
298
|
// eslint-disable-next-line no-unused-vars
|
|
278
299
|
} catch (error) {
|
|
279
300
|
response = await Promise.race([
|
|
280
301
|
fetchResource(resourceURL, { referrer: options.resourceReferrer, referrerPolicy: options.referrerPolicy || undefined, stylesheetURL: options.stylesheetURL || undefined, stylesheetReferrerPolicy: options.stylesheetReferrerPolicy || undefined, headers: { accept } }),
|
|
281
302
|
networkTimeoutPromise
|
|
282
303
|
]);
|
|
304
|
+
setManifestResponse(entry, response, MANIFEST_FETCHED_BY_PAGE);
|
|
283
305
|
}
|
|
284
306
|
} else {
|
|
285
307
|
response = await Promise.race([
|
|
286
308
|
fetchResource(resourceURL, { referrer: options.resourceReferrer, referrerPolicy: options.referrerPolicy || undefined, stylesheetURL: options.stylesheetURL || undefined, stylesheetReferrerPolicy: options.stylesheetReferrerPolicy || undefined, headers: { accept } }),
|
|
287
309
|
networkTimeoutPromise
|
|
288
310
|
]);
|
|
311
|
+
setManifestResponse(entry, response, MANIFEST_FETCHED_BY_PAGE);
|
|
289
312
|
}
|
|
290
|
-
// eslint-disable-next-line no-unused-vars
|
|
291
313
|
} catch (error) {
|
|
314
|
+
setManifestError(entry, error);
|
|
292
315
|
return getFailedFetchResponse(resourceURL, options);
|
|
293
316
|
} finally {
|
|
294
317
|
resolveNetworkTimeoutPromise();
|
|
@@ -299,10 +322,11 @@ function getInstance(utilOptions) {
|
|
|
299
322
|
let buffer;
|
|
300
323
|
try {
|
|
301
324
|
buffer = await response.arrayBuffer();
|
|
302
|
-
// eslint-disable-next-line no-unused-vars
|
|
303
325
|
} catch (error) {
|
|
326
|
+
setManifestError(entry, error);
|
|
304
327
|
return options.inline ? { data: options.asBinary ? helper.EMPTY_RESOURCE : "", resourceURL } : { resourceURL };
|
|
305
328
|
}
|
|
329
|
+
await setManifestContent(entry, buffer);
|
|
306
330
|
resourceURL = response.url || resourceURL;
|
|
307
331
|
let contentType = "", charset;
|
|
308
332
|
try {
|
|
@@ -322,8 +346,15 @@ function getInstance(utilOptions) {
|
|
|
322
346
|
if (!charset && options.charset) {
|
|
323
347
|
charset = options.charset;
|
|
324
348
|
}
|
|
349
|
+
if (entry) {
|
|
350
|
+
entry.contentType = contentType;
|
|
351
|
+
if (charset) {
|
|
352
|
+
entry.charset = charset;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
325
355
|
if (options.asBinary) {
|
|
326
356
|
if ((response.status >= 400 && options.expectedType != EXPECTED_TYPE_IMAGE) || (EXPECTED_TYPES_MEDIA.includes(options.expectedType) && CONTENT_TYPES_HTML.includes(contentType))) {
|
|
357
|
+
setManifestOutcome(entry, MANIFEST_OUTCOME_REJECTED);
|
|
327
358
|
return getFetchResponse(resourceURL, options);
|
|
328
359
|
}
|
|
329
360
|
try {
|
|
@@ -331,19 +362,23 @@ function getInstance(utilOptions) {
|
|
|
331
362
|
log(" // ENDED download url =", resourceURL, "delay =", Date.now() - startTime);
|
|
332
363
|
}
|
|
333
364
|
if (options.maxResourceSizeEnabled && buffer.byteLength > options.maxResourceSize * ONE_MB) {
|
|
365
|
+
setManifestOutcome(entry, MANIFEST_OUTCOME_OVERSIZED);
|
|
334
366
|
return getFetchResponse(resourceURL, options);
|
|
335
367
|
} else {
|
|
368
|
+
setManifestOutcome(entry, MANIFEST_OUTCOME_EMBEDDED);
|
|
336
369
|
return getFetchResponse(resourceURL, options, buffer, null, contentType);
|
|
337
370
|
}
|
|
338
|
-
// eslint-disable-next-line no-unused-vars
|
|
339
371
|
} catch (error) {
|
|
372
|
+
setManifestError(entry, error);
|
|
340
373
|
return getFetchResponse(resourceURL, options);
|
|
341
374
|
}
|
|
342
375
|
} else {
|
|
343
376
|
if (response.status >= 400 && options.expectedType != EXPECTED_TYPE_DOCUMENT) {
|
|
377
|
+
setManifestOutcome(entry, MANIFEST_OUTCOME_REJECTED);
|
|
344
378
|
return getFailedFetchResponse(resourceURL, options);
|
|
345
379
|
}
|
|
346
380
|
if (options.validateTextContentType && contentType && !contentType.startsWith(PREFIX_CONTENT_TYPE_TEXT)) {
|
|
381
|
+
setManifestOutcome(entry, MANIFEST_OUTCOME_REJECTED);
|
|
347
382
|
return getFetchResponse(resourceURL, options);
|
|
348
383
|
}
|
|
349
384
|
if (!charset) {
|
|
@@ -353,14 +388,16 @@ function getInstance(utilOptions) {
|
|
|
353
388
|
log(" // ENDED download url =", resourceURL, "delay =", Date.now() - startTime);
|
|
354
389
|
}
|
|
355
390
|
if (options.maxResourceSizeEnabled && buffer.byteLength > options.maxResourceSize * ONE_MB) {
|
|
391
|
+
setManifestOutcome(entry, MANIFEST_OUTCOME_OVERSIZED);
|
|
356
392
|
return getFetchResponse(resourceURL, options, null, charset);
|
|
357
393
|
} else {
|
|
358
394
|
try {
|
|
359
395
|
const fetchResponse = await getFetchResponse(resourceURL, options, buffer, charset, contentType);
|
|
360
396
|
fetchResponse.referrerPolicy = (response.headers && response.headers.get("referrer-policy")) || undefined;
|
|
397
|
+
setManifestOutcome(entry, MANIFEST_OUTCOME_EMBEDDED);
|
|
361
398
|
return fetchResponse;
|
|
362
|
-
// eslint-disable-next-line no-unused-vars
|
|
363
399
|
} catch (error) {
|
|
400
|
+
setManifestError(entry, error);
|
|
364
401
|
return getFetchResponse(resourceURL, options, null, charset);
|
|
365
402
|
}
|
|
366
403
|
}
|
|
@@ -368,6 +405,60 @@ function getInstance(utilOptions) {
|
|
|
368
405
|
}
|
|
369
406
|
}
|
|
370
407
|
|
|
408
|
+
function setManifestResponse(entry, response, fetchedBy) {
|
|
409
|
+
if (entry) {
|
|
410
|
+
entry.fetchedBy = fetchedBy;
|
|
411
|
+
if (response.url) {
|
|
412
|
+
entry.finalUrl = response.url;
|
|
413
|
+
}
|
|
414
|
+
if (response.status !== undefined) {
|
|
415
|
+
entry.status = response.status;
|
|
416
|
+
}
|
|
417
|
+
const headers = {};
|
|
418
|
+
MANIFEST_HEADER_NAMES.forEach(name => {
|
|
419
|
+
const value = getHeaderValue(response.headers, name);
|
|
420
|
+
if (value) {
|
|
421
|
+
headers[name] = value;
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
if (Object.keys(headers).length) {
|
|
425
|
+
entry.headers = headers;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function getHeaderValue(headers, name) {
|
|
431
|
+
if (headers) {
|
|
432
|
+
if (typeof headers.get == "function") {
|
|
433
|
+
return headers.get(name);
|
|
434
|
+
}
|
|
435
|
+
const key = Object.keys(headers).find(key => key.toLowerCase() == name);
|
|
436
|
+
return key === undefined ? undefined : headers[key];
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async function setManifestContent(entry, buffer) {
|
|
441
|
+
if (entry) {
|
|
442
|
+
entry.size = buffer.byteLength;
|
|
443
|
+
entry.sha256 = await helper.digest("SHA-256", buffer);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function setManifestOutcome(entry, outcome) {
|
|
448
|
+
if (entry) {
|
|
449
|
+
entry.outcome = outcome;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function setManifestError(entry, error) {
|
|
454
|
+
if (entry) {
|
|
455
|
+
entry.outcome = MANIFEST_OUTCOME_FAILED;
|
|
456
|
+
if (error && error.message) {
|
|
457
|
+
entry.error = error.message;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
371
462
|
async function getFailedFetchResponse(resourceURL, options) {
|
|
372
463
|
const response = await getFetchResponse(resourceURL, options);
|
|
373
464
|
response.failed = true;
|
|
@@ -398,7 +489,7 @@ async function getFetchResponse(resourceURL, options, data, charset, contentType
|
|
|
398
489
|
charset = "utf-8";
|
|
399
490
|
data = new TextDecoder(charset).decode(data);
|
|
400
491
|
}
|
|
401
|
-
data = data.replace(
|
|
492
|
+
data = data.replace(/^\ufeff/, "");
|
|
402
493
|
}
|
|
403
494
|
} else if (options.inline) {
|
|
404
495
|
data = options.asBinary ? helper.EMPTY_RESOURCE : "";
|
package/package.json
CHANGED
|
@@ -237,6 +237,7 @@ function initResponse(message) {
|
|
|
237
237
|
frameData.scrollPosition = messageFrameData.scrollPosition;
|
|
238
238
|
frameData.scrolling = messageFrameData.scrolling;
|
|
239
239
|
frameData.adoptedStyleSheets = messageFrameData.adoptedStyleSheets;
|
|
240
|
+
frameData.manifestData = messageFrameData.manifestData;
|
|
240
241
|
}
|
|
241
242
|
});
|
|
242
243
|
const remainingFrames = windowData.frames.filter(frameData => !frameData.processed).length;
|
|
@@ -459,6 +460,7 @@ function getFrameData(document, win, windowId, options, scrolling) {
|
|
|
459
460
|
scrollPosition: docData.scrollPosition,
|
|
460
461
|
scrolling,
|
|
461
462
|
adoptedStyleSheets: docData.adoptedStyleSheets,
|
|
463
|
+
manifestData: docData.manifestData,
|
|
462
464
|
processed: true
|
|
463
465
|
};
|
|
464
466
|
}
|
package/single-file.js
CHANGED
|
@@ -131,5 +131,14 @@ async function getPageData(options = {}, initOptions, doc, win) {
|
|
|
131
131
|
}
|
|
132
132
|
pageData.content = Array.from(new Uint8Array(arrayBuffer));
|
|
133
133
|
}
|
|
134
|
+
if (pageData.manifest) {
|
|
135
|
+
const content = options.compressContent ? new Uint8Array(pageData.content) : pageData.content;
|
|
136
|
+
pageData.manifest.output = {
|
|
137
|
+
filename: pageData.filename,
|
|
138
|
+
mimeType: pageData.mimeType,
|
|
139
|
+
size: options.compressContent ? content.length : helper.getContentSize(content),
|
|
140
|
+
sha256: await helper.digest("SHA-256", content)
|
|
141
|
+
};
|
|
142
|
+
}
|
|
134
143
|
return pageData;
|
|
135
144
|
}
|