single-file-core 1.5.129 → 1.6.0
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 +27 -1
- package/core/index.js +34 -21
- package/core/infobar.js +7 -7
- package/core/lib/processor-helper-inline.js +1 -1
- package/core/lib/processor-helper.js +1 -1
- package/package.json +1 -1
- package/processors/compression/compression.js +2 -1
- package/processors/frame-tree/content/content-frame-tree.js +1 -1
- package/processors/hooks/content/content-hooks-frames-web.js +48 -38
- package/processors/hooks/content/content-hooks-frames.js +28 -27
- package/processors/lazy/content/content-lazy-loader.js +10 -10
- package/single-file.js +10 -9
- package/test/capture/canonical-link.js +50 -0
- package/test/capture/common.js +1 -0
- package/test/capture/deferred-content-options.js +71 -0
- package/test/capture/dom.js +13 -1
- package/test/capture/maff-metadata.js +205 -0
- package/test/capture/script-uri-sanitization.js +82 -0
- package/test/sfz-harness/pages-archive.js +5 -2
- package/vendor/zip/z-worker.js +1 -1
- package/vendor/zip/zip.js +707 -951
- package/vendor/zip/zip.min.js +1 -1
- package/zip-build/package-lock.json +4 -4
- package/zip-build/package.json +1 -1
- package/zip-build/reserved-property-names.json +4 -0
- package/zip-build/rollup.config.js +1 -1
package/core/helper.js
CHANGED
|
@@ -82,6 +82,15 @@ const DEFAULT_REPLACEMENT_CHARACTER = "_";
|
|
|
82
82
|
const DEFAULT_REPLACEMENT_CHARACTERS = ["~", "+", "?", "%", "*", ":", "|", """, "<", ">", "\"];
|
|
83
83
|
const CHARACTER_CLASS_SPECIAL_CHARACTERS = ["[", "]", "^", "-", "\\"];
|
|
84
84
|
const NESTING_TRACK_ID_ATTRIBUTE_NAME = "data-sf-nesting-track-id";
|
|
85
|
+
const DEPRECATED_OPTION_NAMES = {
|
|
86
|
+
loadDeferredImages: "loadDeferredContent",
|
|
87
|
+
loadDeferredImagesMaxIdleTime: "loadDeferredContentMaxIdleTime",
|
|
88
|
+
loadDeferredImagesBlockCookies: "loadDeferredContentBlockCookies",
|
|
89
|
+
loadDeferredImagesBlockStorage: "loadDeferredContentBlockStorage",
|
|
90
|
+
loadDeferredImagesKeepZoomLevel: "loadDeferredContentKeepZoomLevel",
|
|
91
|
+
loadDeferredImagesDispatchScrollEvent: "loadDeferredContentDispatchScrollEvent",
|
|
92
|
+
loadDeferredImagesBeforeFrames: "loadDeferredContentBeforeFrames"
|
|
93
|
+
};
|
|
85
94
|
const addEventListener = (type, listener, options) => globalThis.addEventListener(type, listener, options);
|
|
86
95
|
const removeEventListener = (type, listener, options) => globalThis.removeEventListener(type, listener, options);
|
|
87
96
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -117,6 +126,7 @@ export {
|
|
|
117
126
|
parseDocContent,
|
|
118
127
|
markInvalidNesting,
|
|
119
128
|
fixInvalidNesting,
|
|
129
|
+
normalizeOptions,
|
|
120
130
|
ON_BEFORE_CAPTURE_EVENT_NAME,
|
|
121
131
|
ON_AFTER_CAPTURE_EVENT_NAME,
|
|
122
132
|
WIN_ID_ATTRIBUTE_NAME,
|
|
@@ -153,6 +163,22 @@ export {
|
|
|
153
163
|
// lossy format: the same frame costs an order of magnitude less than as a PNG.
|
|
154
164
|
// toDataURL falls back to PNG without reporting it when it does not support the
|
|
155
165
|
// format, so the type it returned is tested instead of assumed
|
|
166
|
+
function normalizeOptions(options) {
|
|
167
|
+
let normalizedOptions = options;
|
|
168
|
+
if (options) {
|
|
169
|
+
Object.keys(DEPRECATED_OPTION_NAMES).forEach(deprecatedName => {
|
|
170
|
+
const optionName = DEPRECATED_OPTION_NAMES[deprecatedName];
|
|
171
|
+
if (options[deprecatedName] !== undefined && options[optionName] === undefined) {
|
|
172
|
+
if (normalizedOptions == options) {
|
|
173
|
+
normalizedOptions = Object.assign({}, options);
|
|
174
|
+
}
|
|
175
|
+
normalizedOptions[optionName] = options[deprecatedName];
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return normalizedOptions;
|
|
180
|
+
}
|
|
181
|
+
|
|
156
182
|
function getPosterDataURI(canvasElement) {
|
|
157
183
|
for (const contentType of POSTER_CONTENT_TYPES) {
|
|
158
184
|
const dataURI = canvasElement.toDataURL(contentType, POSTER_QUALITY);
|
|
@@ -535,7 +561,7 @@ function getResourcesInfo(win, doc, element, options, data, elementHidden, compu
|
|
|
535
561
|
const imageData = {
|
|
536
562
|
currentSrc: elementHidden ?
|
|
537
563
|
EMPTY_RESOURCE :
|
|
538
|
-
(options.
|
|
564
|
+
(options.loadDeferredContent && element.getAttribute(LAZY_SRC_ATTRIBUTE_NAME)) || element.currentSrc
|
|
539
565
|
};
|
|
540
566
|
data.images.push(imageData);
|
|
541
567
|
element.setAttribute(IMAGE_ATTRIBUTE_NAME, data.images.length - 1);
|
package/core/index.js
CHANGED
|
@@ -31,6 +31,7 @@ const DEBUG = false;
|
|
|
31
31
|
const Set = globalThis.Set;
|
|
32
32
|
const Map = globalThis.Map;
|
|
33
33
|
const JSON = globalThis.JSON;
|
|
34
|
+
const URL = globalThis.URL;
|
|
34
35
|
|
|
35
36
|
let util;
|
|
36
37
|
|
|
@@ -102,7 +103,7 @@ const FINALIZE_STAGE = 4;
|
|
|
102
103
|
const STAGES = [{
|
|
103
104
|
sequential: [
|
|
104
105
|
{ action: "preProcessPage" },
|
|
105
|
-
{ option: "
|
|
106
|
+
{ option: "loadDeferredContentKeepZoomLevel", action: "resetZoomLevel" },
|
|
106
107
|
{ action: "replaceStyleContents" },
|
|
107
108
|
{ action: "replaceInvalidElements" },
|
|
108
109
|
{ action: "resetCharsetMeta" },
|
|
@@ -160,7 +161,7 @@ const STAGES = [{
|
|
|
160
161
|
{ action: "cleanupPage" }
|
|
161
162
|
],
|
|
162
163
|
parallel: [
|
|
163
|
-
{ option: "
|
|
164
|
+
{ option: "readMaffMetadata", action: "readMAFFMetaData" },
|
|
164
165
|
{ action: "setDocInfo" }
|
|
165
166
|
]
|
|
166
167
|
}, {
|
|
@@ -421,6 +422,9 @@ const SHADOWROOT_DELEGATES_FOCUS = "shadowrootdelegatesfocus";
|
|
|
421
422
|
const SHADOWROOT_CLONABLE = "shadowrootclonable";
|
|
422
423
|
const SHADOWROOT_SERIALIZABLE = "shadowrootserializable";
|
|
423
424
|
const SCRIPT_OPTIONS = "data-single-file-options";
|
|
425
|
+
const JAVASCRIPT_URI_PROTOCOL = "javascript:";
|
|
426
|
+
const DISABLED_SCRIPT_URI = "javascript:void(0)";
|
|
427
|
+
const SCRIPT_URI_ATTRIBUTE_NAMES = ["href", "src", "action", "formaction", "data"];
|
|
424
428
|
const UTF8_CHARSET = "utf-8";
|
|
425
429
|
const TAINTED_CANVAS_WARNING_MESSAGE = "SingleFile: canvas elements tainted by a cross-origin resource, dropped from the page:";
|
|
426
430
|
|
|
@@ -448,7 +452,7 @@ class Processor {
|
|
|
448
452
|
initialize() {
|
|
449
453
|
this.options.saveDate = new Date();
|
|
450
454
|
this.options.saveUrl = this.options.url;
|
|
451
|
-
if (this.options.
|
|
455
|
+
if (this.options.readMaffMetadata) {
|
|
452
456
|
this.maffMetaDataPromise = this.batchRequest.addURL(util.resolveURL("index.rdf", this.options.baseURI || this.options.url), { expectedType: "document" });
|
|
453
457
|
}
|
|
454
458
|
this.maxResources = this.batchRequest.getMaxResources();
|
|
@@ -497,7 +501,7 @@ class Processor {
|
|
|
497
501
|
}
|
|
498
502
|
this.workStyleElement = this.doc.createElement("style");
|
|
499
503
|
this.doc.body.appendChild(this.workStyleElement);
|
|
500
|
-
this.onEventAttributeNames = getOnEventAttributeNames(this.doc);
|
|
504
|
+
this.onEventAttributeNames = new Set(getOnEventAttributeNames(this.doc));
|
|
501
505
|
}
|
|
502
506
|
|
|
503
507
|
finalize() {
|
|
@@ -637,7 +641,7 @@ class Processor {
|
|
|
637
641
|
imgElement.dataset.singleFileOriginURL = imgElement.getAttribute("src");
|
|
638
642
|
imgElement.setAttribute("src", imageData.currentSrc);
|
|
639
643
|
}
|
|
640
|
-
if (this.options.
|
|
644
|
+
if (this.options.loadDeferredContent) {
|
|
641
645
|
if ((!imgElement.getAttribute("src") || imgElement.getAttribute("src") == util.EMPTY_RESOURCE) && imgElement.getAttribute("data-src")) {
|
|
642
646
|
imageData.src = imgElement.dataset.src;
|
|
643
647
|
imgElement.setAttribute("src", imgElement.dataset.src);
|
|
@@ -648,7 +652,7 @@ class Processor {
|
|
|
648
652
|
}
|
|
649
653
|
}
|
|
650
654
|
});
|
|
651
|
-
if (this.options.
|
|
655
|
+
if (this.options.loadDeferredContent) {
|
|
652
656
|
this.doc.querySelectorAll("img[data-srcset]").forEach(imgElement => {
|
|
653
657
|
if (!imgElement.getAttribute("srcset") && imgElement.getAttribute("data-srcset")) {
|
|
654
658
|
imgElement.setAttribute("srcset", imgElement.dataset.srcset);
|
|
@@ -676,7 +680,7 @@ class Processor {
|
|
|
676
680
|
optionsElement.type = "application/json";
|
|
677
681
|
optionsElement.setAttribute(SCRIPT_OPTIONS, "");
|
|
678
682
|
optionsElement.textContent = JSON.stringify({
|
|
679
|
-
saveUrl: this.options.
|
|
683
|
+
saveUrl: this.options.saveUrl,
|
|
680
684
|
saveDate: this.options.saveDate.getTime(),
|
|
681
685
|
visitDate: (this.options.visitDate || this.options.saveDate).getTime(),
|
|
682
686
|
filenameTemplate: this.options.filenameTemplate,
|
|
@@ -852,18 +856,16 @@ class Processor {
|
|
|
852
856
|
}
|
|
853
857
|
|
|
854
858
|
removeEmbedScripts() {
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
element.setAttribute("src", DISABLED_SCRIPT);
|
|
866
|
-
}
|
|
859
|
+
this.doc.querySelectorAll("*").forEach(element => {
|
|
860
|
+
Array.from(element.attributes).forEach(attribute => {
|
|
861
|
+
const localName = attribute.localName || attribute.name;
|
|
862
|
+
const attributeName = localName.toLowerCase();
|
|
863
|
+
if (this.onEventAttributeNames.has(attributeName)) {
|
|
864
|
+
element.removeAttributeNS(attribute.namespaceURI, localName);
|
|
865
|
+
} else if (SCRIPT_URI_ATTRIBUTE_NAMES.includes(attributeName) && isScriptURI(attribute.value)) {
|
|
866
|
+
element.setAttributeNS(attribute.namespaceURI, attribute.name, DISABLED_SCRIPT_URI);
|
|
867
|
+
}
|
|
868
|
+
});
|
|
867
869
|
});
|
|
868
870
|
const scriptElements = this.doc.querySelectorAll("script:not([type=\"application/ld+json\"]):not([" + SCRIPT_OPTIONS + "])");
|
|
869
871
|
this.stats.set("discarded", "scripts", scriptElements.length);
|
|
@@ -1647,7 +1649,7 @@ class Processor {
|
|
|
1647
1649
|
this.doc.documentElement.style.removeProperty("-sf-min-height");
|
|
1648
1650
|
}
|
|
1649
1651
|
|
|
1650
|
-
async
|
|
1652
|
+
async readMAFFMetaData() {
|
|
1651
1653
|
const maffMetaData = await this.maffMetaDataPromise;
|
|
1652
1654
|
if (maffMetaData && maffMetaData.content) {
|
|
1653
1655
|
const NAMESPACE_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
|
|
@@ -1655,7 +1657,10 @@ class Processor {
|
|
|
1655
1657
|
const originalURLElement = maffDoc.querySelector("RDF > Description > originalurl");
|
|
1656
1658
|
const archiveTimeElement = maffDoc.querySelector("RDF > Description > archivetime");
|
|
1657
1659
|
if (originalURLElement) {
|
|
1658
|
-
|
|
1660
|
+
const value = originalURLElement.getAttributeNS(NAMESPACE_RDF, "resource");
|
|
1661
|
+
if (value) {
|
|
1662
|
+
this.options.saveUrl = value;
|
|
1663
|
+
}
|
|
1659
1664
|
}
|
|
1660
1665
|
if (archiveTimeElement) {
|
|
1661
1666
|
const value = archiveTimeElement.getAttributeNS(NAMESPACE_RDF, "resource");
|
|
@@ -1709,6 +1714,14 @@ function normalizeURL(url) {
|
|
|
1709
1714
|
}
|
|
1710
1715
|
}
|
|
1711
1716
|
|
|
1717
|
+
function isScriptURI(value) {
|
|
1718
|
+
try {
|
|
1719
|
+
return new URL(value).protocol == JAVASCRIPT_URI_PROTOCOL;
|
|
1720
|
+
} catch {
|
|
1721
|
+
return false;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1712
1725
|
function getOnEventAttributeNames(doc) {
|
|
1713
1726
|
const element = doc.body || doc.createElement("div");
|
|
1714
1727
|
const attributeNames = [];
|
package/core/infobar.js
CHANGED
|
@@ -141,28 +141,28 @@ const INFOBAR_STYLES = `
|
|
|
141
141
|
|
|
142
142
|
@keyframes flash {
|
|
143
143
|
0%, 100% {
|
|
144
|
-
|
|
144
|
+
background-color: #737373;
|
|
145
145
|
}
|
|
146
146
|
50% {
|
|
147
|
-
|
|
147
|
+
background-color: #dd6a00;
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
@keyframes ripple {
|
|
152
152
|
0% {
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
transform: scale(1);
|
|
154
|
+
opacity: 1;
|
|
155
155
|
}
|
|
156
156
|
45%, 100% {
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
transform: scale(2);
|
|
158
|
+
opacity: 0;
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
@media (prefers-reduced-motion: reduce) {
|
|
163
163
|
.infobar,
|
|
164
164
|
.infobar:not(:focus-within):not(.infobar-focus)::after {
|
|
165
|
-
|
|
165
|
+
animation-name: none;
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
|
|
@@ -562,7 +562,7 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
562
562
|
}
|
|
563
563
|
|
|
564
564
|
setMetaCSP(metaElement) {
|
|
565
|
-
metaElement.content = "default-src 'none'; font-src 'self' data:; img-src 'self' data:; style-src 'unsafe-inline'; media-src 'self' data:; script-src 'unsafe-inline' data:; object-src 'self' data:; frame-src 'self' data:;";
|
|
565
|
+
metaElement.content = "default-src 'none'; font-src 'self' data:; img-src 'self' data:; style-src 'unsafe-inline'; media-src 'self' data:; script-src 'unsafe-inline' data:; object-src 'self' data:; frame-src 'self' data:; form-action 'none'; base-uri 'none';";
|
|
566
566
|
}
|
|
567
567
|
|
|
568
568
|
removeUnusedStylesheets(doc) {
|
|
@@ -488,7 +488,7 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
488
488
|
}
|
|
489
489
|
|
|
490
490
|
setMetaCSP(metaElement) {
|
|
491
|
-
metaElement.content = "default-src 'none'; connect-src 'self' data: blob:; font-src 'self' data: blob:; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline' data: blob:; frame-src 'self' data: blob:; media-src 'self' data: blob:; script-src 'self' 'unsafe-inline' data: blob:; object-src 'self' data: blob:;";
|
|
491
|
+
metaElement.content = "default-src 'none'; connect-src 'self' data: blob:; font-src 'self' data: blob:; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline' data: blob:; frame-src 'self' data: blob:; media-src 'self' data: blob:; script-src 'self' 'unsafe-inline' data: blob:; object-src 'self' data: blob:; form-action 'none'; base-uri 'none';";
|
|
492
492
|
}
|
|
493
493
|
|
|
494
494
|
removeUnusedStylesheets() {
|
package/package.json
CHANGED
|
@@ -114,7 +114,7 @@ async function onMessage(event) {
|
|
|
114
114
|
}
|
|
115
115
|
if (!TOP_WINDOW) {
|
|
116
116
|
globalThis.stop();
|
|
117
|
-
if (message.options.
|
|
117
|
+
if (message.options.loadDeferredContent) {
|
|
118
118
|
lazy.process(message.options);
|
|
119
119
|
}
|
|
120
120
|
await initRequestAsync(message);
|
|
@@ -23,12 +23,12 @@
|
|
|
23
23
|
|
|
24
24
|
(() => {
|
|
25
25
|
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const
|
|
26
|
+
const LOAD_DEFERRED_CONTENT_START_EVENT = "single-file-load-deferred-content-start";
|
|
27
|
+
const LOAD_DEFERRED_CONTENT_END_EVENT = "single-file-load-deferred-content-end";
|
|
28
|
+
const LOAD_DEFERRED_CONTENT_KEEP_ZOOM_LEVEL_START_EVENT = "single-file-load-deferred-content-keep-zoom-level-start";
|
|
29
|
+
const LOAD_DEFERRED_CONTENT_KEEP_ZOOM_LEVEL_END_EVENT = "single-file-load-deferred-content-keep-zoom-level-end";
|
|
30
|
+
const LOAD_DEFERRED_CONTENT_RESET_ZOOM_LEVEL_EVENT = "single-file-load-deferred-content-keep-zoom-level-reset";
|
|
31
|
+
const LOAD_DEFERRED_CONTENT_RESET_EVENT = "single-file-load-deferred-content-reset";
|
|
32
32
|
const BLOCK_COOKIES_START_EVENT = "single-file-block-cookies-start";
|
|
33
33
|
const BLOCK_COOKIES_END_EVENT = "single-file-block-cookies-end";
|
|
34
34
|
const BLOCK_STORAGE_START_EVENT = "single-file-block-storage-start";
|
|
@@ -89,12 +89,12 @@
|
|
|
89
89
|
new MutationObserver(init).observe(document, { childList: true });
|
|
90
90
|
|
|
91
91
|
function init() {
|
|
92
|
-
document.addEventListener(
|
|
93
|
-
document.addEventListener(
|
|
94
|
-
document.addEventListener(
|
|
95
|
-
document.addEventListener(
|
|
96
|
-
document.addEventListener(
|
|
97
|
-
document.addEventListener(
|
|
92
|
+
document.addEventListener(LOAD_DEFERRED_CONTENT_START_EVENT, onLoadDeferredContentStart);
|
|
93
|
+
document.addEventListener(LOAD_DEFERRED_CONTENT_KEEP_ZOOM_LEVEL_START_EVENT, onLoadDeferredContentKeepZoomLevelStart);
|
|
94
|
+
document.addEventListener(LOAD_DEFERRED_CONTENT_END_EVENT, onLoadDeferredContentEnd);
|
|
95
|
+
document.addEventListener(LOAD_DEFERRED_CONTENT_KEEP_ZOOM_LEVEL_END_EVENT, onLoadDeferredContentKeepZoomLevelEnd);
|
|
96
|
+
document.addEventListener(LOAD_DEFERRED_CONTENT_RESET_EVENT, resetScreenSize);
|
|
97
|
+
document.addEventListener(LOAD_DEFERRED_CONTENT_RESET_ZOOM_LEVEL_EVENT, onLoadDeferredContentResetZoomLevel);
|
|
98
98
|
document.addEventListener(DISPATCH_SCROLL_START_EVENT, onDispatchScrollStart);
|
|
99
99
|
document.addEventListener(DISPATCH_SCROLL_END_EVENT, onDispatchScrollEnd);
|
|
100
100
|
document.addEventListener(BLOCK_COOKIES_START_EVENT, onBlockCookiesStart);
|
|
@@ -107,23 +107,33 @@
|
|
|
107
107
|
document.addEventListener(BOOTSTRAP_EVENT, onBootstrap);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
function
|
|
111
|
-
|
|
110
|
+
function onLoadDeferredContentStart(event) {
|
|
111
|
+
loadDeferredContentStart(false, getMinZoomFactor(event));
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
function
|
|
115
|
-
|
|
114
|
+
function onLoadDeferredContentKeepZoomLevelStart(event) {
|
|
115
|
+
loadDeferredContentStart(true, getMinZoomFactor(event));
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
function
|
|
119
|
-
|
|
118
|
+
function getMinZoomFactor(event) {
|
|
119
|
+
try {
|
|
120
|
+
const { minZoomFactor } = JSON.parse(event.detail);
|
|
121
|
+
return minZoomFactor > 0 && minZoomFactor <= 1 ? minZoomFactor : 0;
|
|
122
|
+
// eslint-disable-next-line no-unused-vars
|
|
123
|
+
} catch (error) {
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function onLoadDeferredContentEnd() {
|
|
129
|
+
loadDeferredContentEnd();
|
|
120
130
|
}
|
|
121
131
|
|
|
122
|
-
function
|
|
123
|
-
|
|
132
|
+
function onLoadDeferredContentKeepZoomLevelEnd() {
|
|
133
|
+
loadDeferredContentEnd(true);
|
|
124
134
|
}
|
|
125
135
|
|
|
126
|
-
function
|
|
136
|
+
function onLoadDeferredContentResetZoomLevel() {
|
|
127
137
|
const transform = document.documentElement.style.getPropertyValue("-sf-transform");
|
|
128
138
|
const transformPriority = document.documentElement.style.getPropertyPriority("-sf-transform");
|
|
129
139
|
const transformOrigin = document.documentElement.style.getPropertyValue("-sf-transform-origin");
|
|
@@ -211,34 +221,34 @@
|
|
|
211
221
|
}
|
|
212
222
|
}
|
|
213
223
|
|
|
214
|
-
function
|
|
224
|
+
function loadDeferredContentStart(keepZoomLevel, minZoomFactor) {
|
|
215
225
|
const scrollingElement = document.scrollingElement || document.documentElement;
|
|
216
226
|
const clientHeight = scrollingElement.clientHeight;
|
|
217
227
|
const clientWidth = scrollingElement.clientWidth;
|
|
218
|
-
const
|
|
219
|
-
const
|
|
228
|
+
const maxScrollY = Math.max(scrollingElement.scrollHeight - clientHeight, clientHeight);
|
|
229
|
+
const maxScrollX = Math.max(scrollingElement.scrollWidth - clientWidth, clientWidth);
|
|
220
230
|
document.querySelectorAll("[loading=lazy]").forEach(element => {
|
|
221
231
|
element.loading = "eager";
|
|
222
232
|
element.setAttribute(LAZY_LOAD_ATTRIBUTE, "");
|
|
223
233
|
});
|
|
224
|
-
scrollingElement.__defineGetter__("clientHeight", () =>
|
|
225
|
-
scrollingElement.__defineGetter__("clientWidth", () =>
|
|
226
|
-
screen.__defineGetter__("height", () =>
|
|
227
|
-
screen.__defineGetter__("width", () =>
|
|
234
|
+
scrollingElement.__defineGetter__("clientHeight", () => maxScrollY);
|
|
235
|
+
scrollingElement.__defineGetter__("clientWidth", () => maxScrollX);
|
|
236
|
+
screen.__defineGetter__("height", () => maxScrollY);
|
|
237
|
+
screen.__defineGetter__("width", () => maxScrollX);
|
|
228
238
|
globalThis._singleFile_innerHeight = globalThis.innerHeight;
|
|
229
239
|
globalThis._singleFile_innerWidth = globalThis.innerWidth;
|
|
230
|
-
globalThis.__defineGetter__("innerHeight", () =>
|
|
231
|
-
globalThis.__defineGetter__("innerWidth", () =>
|
|
240
|
+
globalThis.__defineGetter__("innerHeight", () => maxScrollY);
|
|
241
|
+
globalThis.__defineGetter__("innerWidth", () => maxScrollX);
|
|
232
242
|
if (!keepZoomLevel) {
|
|
233
243
|
if (!globalThis._singleFile_getBoundingClientRect) {
|
|
234
244
|
globalThis._singleFile_getBoundingClientRect = Element.prototype.getBoundingClientRect;
|
|
235
245
|
Element.prototype.getBoundingClientRect = function () {
|
|
236
246
|
const boundingRect = globalThis._singleFile_getBoundingClientRect.call(this);
|
|
237
247
|
if (this == scrollingElement) {
|
|
238
|
-
boundingRect.__defineGetter__("height", () =>
|
|
239
|
-
boundingRect.__defineGetter__("bottom", () =>
|
|
240
|
-
boundingRect.__defineGetter__("width", () =>
|
|
241
|
-
boundingRect.__defineGetter__("right", () =>
|
|
248
|
+
boundingRect.__defineGetter__("height", () => maxScrollY);
|
|
249
|
+
boundingRect.__defineGetter__("bottom", () => maxScrollY + boundingRect.top);
|
|
250
|
+
boundingRect.__defineGetter__("width", () => maxScrollX);
|
|
251
|
+
boundingRect.__defineGetter__("right", () => maxScrollX + boundingRect.left);
|
|
242
252
|
}
|
|
243
253
|
return boundingRect;
|
|
244
254
|
};
|
|
@@ -283,9 +293,9 @@
|
|
|
283
293
|
setFunctionName(ImageWrapper, "Image");
|
|
284
294
|
globalThis.__defineGetter__("Image", () => ImageWrapper);
|
|
285
295
|
}
|
|
286
|
-
const verticalZoomFactor = clientHeight /
|
|
287
|
-
const horizontalZoomFactor = clientWidth /
|
|
288
|
-
const zoomFactor = Math.min(verticalZoomFactor, horizontalZoomFactor);
|
|
296
|
+
const verticalZoomFactor = clientHeight / maxScrollY;
|
|
297
|
+
const horizontalZoomFactor = clientWidth / maxScrollX;
|
|
298
|
+
const zoomFactor = Math.max(Math.min(verticalZoomFactor, horizontalZoomFactor), minZoomFactor || 0);
|
|
289
299
|
const scrollPosition = { x: globalThis.scrollX, y: globalThis.scrollY };
|
|
290
300
|
if (zoomFactor < 1) {
|
|
291
301
|
const transform = document.documentElement.style.getPropertyValue("transform");
|
|
@@ -336,7 +346,7 @@
|
|
|
336
346
|
}
|
|
337
347
|
}
|
|
338
348
|
|
|
339
|
-
function
|
|
349
|
+
function loadDeferredContentEnd(keepZoomLevel) {
|
|
340
350
|
document.querySelectorAll("[" + LAZY_LOAD_ATTRIBUTE + "]").forEach(element => {
|
|
341
351
|
element.loading = "lazy";
|
|
342
352
|
element.removeAttribute(LAZY_LOAD_ATTRIBUTE);
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
* Source.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
24
|
+
const LOAD_DEFERRED_CONTENT_START_EVENT = "single-file-load-deferred-content-start";
|
|
25
|
+
const LOAD_DEFERRED_CONTENT_END_EVENT = "single-file-load-deferred-content-end";
|
|
26
|
+
const LOAD_DEFERRED_CONTENT_KEEP_ZOOM_LEVEL_START_EVENT = "single-file-load-deferred-content-keep-zoom-level-start";
|
|
27
|
+
const LOAD_DEFERRED_CONTENT_KEEP_ZOOM_LEVEL_END_EVENT = "single-file-load-deferred-content-keep-zoom-level-end";
|
|
28
|
+
const LOAD_DEFERRED_CONTENT_RESET_ZOOM_LEVEL_EVENT = "single-file-load-deferred-content-keep-zoom-level-reset";
|
|
29
|
+
const LOAD_DEFERRED_CONTENT_RESET_EVENT = "single-file-load-deferred-content-reset";
|
|
30
30
|
const BLOCK_COOKIES_START_EVENT = "single-file-block-cookies-start";
|
|
31
31
|
const BLOCK_COOKIES_END_EVENT = "single-file-block-cookies-end";
|
|
32
32
|
const DISPATCH_SCROLL_START_EVENT = "single-file-dispatch-scroll-event-start";
|
|
@@ -98,9 +98,9 @@ function onNewWorklet(event) {
|
|
|
98
98
|
export {
|
|
99
99
|
getFontsData,
|
|
100
100
|
getWorkletsData,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
loadDeferredContentStart,
|
|
102
|
+
loadDeferredContentEnd,
|
|
103
|
+
loadDeferredContentResetZoomLevel,
|
|
104
104
|
LOAD_IMAGE_EVENT,
|
|
105
105
|
IMAGE_LOADED_EVENT
|
|
106
106
|
};
|
|
@@ -113,44 +113,45 @@ function getWorkletsData() {
|
|
|
113
113
|
return Array.from(worklets.values());
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
function
|
|
117
|
-
if (options.
|
|
116
|
+
function loadDeferredContentStart(options) {
|
|
117
|
+
if (options.loadDeferredContentBlockCookies) {
|
|
118
118
|
document.dispatchEvent(new CustomEvent(BLOCK_COOKIES_START_EVENT));
|
|
119
119
|
}
|
|
120
|
-
if (options.
|
|
120
|
+
if (options.loadDeferredContentBlockStorage) {
|
|
121
121
|
document.dispatchEvent(new CustomEvent(BLOCK_STORAGE_START_EVENT));
|
|
122
122
|
}
|
|
123
|
-
if (options.
|
|
123
|
+
if (options.loadDeferredContentDispatchScrollEvent) {
|
|
124
124
|
document.dispatchEvent(new CustomEvent(DISPATCH_SCROLL_START_EVENT));
|
|
125
125
|
}
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
const detail = JSON.stringify({ minZoomFactor: options.loadDeferredContentMinZoomFactor });
|
|
127
|
+
if (options.loadDeferredContentKeepZoomLevel) {
|
|
128
|
+
document.dispatchEvent(new CustomEvent(LOAD_DEFERRED_CONTENT_KEEP_ZOOM_LEVEL_START_EVENT, { detail }));
|
|
128
129
|
} else {
|
|
129
|
-
document.dispatchEvent(new CustomEvent(
|
|
130
|
+
document.dispatchEvent(new CustomEvent(LOAD_DEFERRED_CONTENT_START_EVENT, { detail }));
|
|
130
131
|
}
|
|
131
132
|
}
|
|
132
133
|
|
|
133
|
-
function
|
|
134
|
-
if (options.
|
|
134
|
+
function loadDeferredContentEnd(options) {
|
|
135
|
+
if (options.loadDeferredContentBlockCookies) {
|
|
135
136
|
document.dispatchEvent(new CustomEvent(BLOCK_COOKIES_END_EVENT));
|
|
136
137
|
}
|
|
137
|
-
if (options.
|
|
138
|
+
if (options.loadDeferredContentBlockStorage) {
|
|
138
139
|
document.dispatchEvent(new CustomEvent(BLOCK_STORAGE_END_EVENT));
|
|
139
140
|
}
|
|
140
|
-
if (options.
|
|
141
|
+
if (options.loadDeferredContentDispatchScrollEvent) {
|
|
141
142
|
document.dispatchEvent(new CustomEvent(DISPATCH_SCROLL_END_EVENT));
|
|
142
143
|
}
|
|
143
|
-
if (options.
|
|
144
|
-
document.dispatchEvent(new CustomEvent(
|
|
144
|
+
if (options.loadDeferredContentKeepZoomLevel) {
|
|
145
|
+
document.dispatchEvent(new CustomEvent(LOAD_DEFERRED_CONTENT_KEEP_ZOOM_LEVEL_END_EVENT));
|
|
145
146
|
} else {
|
|
146
|
-
document.dispatchEvent(new CustomEvent(
|
|
147
|
+
document.dispatchEvent(new CustomEvent(LOAD_DEFERRED_CONTENT_END_EVENT));
|
|
147
148
|
}
|
|
148
149
|
}
|
|
149
150
|
|
|
150
|
-
function
|
|
151
|
-
if (options.
|
|
152
|
-
document.dispatchEvent(new CustomEvent(
|
|
151
|
+
function loadDeferredContentResetZoomLevel(options) {
|
|
152
|
+
if (options.loadDeferredContentKeepZoomLevel) {
|
|
153
|
+
document.dispatchEvent(new CustomEvent(LOAD_DEFERRED_CONTENT_RESET_ZOOM_LEVEL_EVENT));
|
|
153
154
|
} else {
|
|
154
|
-
document.dispatchEvent(new CustomEvent(
|
|
155
|
+
document.dispatchEvent(new CustomEvent(LOAD_DEFERRED_CONTENT_RESET_EVENT));
|
|
155
156
|
}
|
|
156
157
|
}
|
|
@@ -81,7 +81,7 @@ async function process(options) {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
function resetZoomLevel(options) {
|
|
84
|
-
hooksFrames.
|
|
84
|
+
hooksFrames.loadDeferredContentResetZoomLevel(options);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function triggerLazyLoading(options) {
|
|
@@ -112,12 +112,12 @@ function triggerLazyLoading(options) {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
});
|
|
115
|
-
await setIdleTimeout(options.
|
|
115
|
+
await setIdleTimeout(options.loadDeferredContentMaxIdleTime * 2);
|
|
116
116
|
await deferForceLazyLoadEnd(observer, options, cleanupAndResolve);
|
|
117
117
|
observer.observe(document, { subtree: true, childList: true, attributes: true });
|
|
118
118
|
document.addEventListener(hooksFrames.LOAD_IMAGE_EVENT, onImageLoadEvent);
|
|
119
119
|
document.addEventListener(hooksFrames.IMAGE_LOADED_EVENT, onImageLoadedEvent);
|
|
120
|
-
hooksFrames.
|
|
120
|
+
hooksFrames.loadDeferredContentStart(options);
|
|
121
121
|
|
|
122
122
|
async function setIdleTimeout(delay) {
|
|
123
123
|
await setAsyncTimeout("idleTimeout", async () => {
|
|
@@ -130,7 +130,7 @@ function triggerLazyLoading(options) {
|
|
|
130
130
|
clearAsyncTimeout("idleTimeout");
|
|
131
131
|
await setIdleTimeout(Math.max(500, delay / 2));
|
|
132
132
|
}
|
|
133
|
-
}, delay
|
|
133
|
+
}, delay);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
function onResourceLoad(event) {
|
|
@@ -167,28 +167,28 @@ function triggerLazyLoading(options) {
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
async function deferLazyLoadEnd(observer, options, resolve) {
|
|
170
|
-
await setAsyncTimeout("loadTimeout", () => lazyLoadEnd(observer, options, resolve), options.
|
|
170
|
+
await setAsyncTimeout("loadTimeout", () => lazyLoadEnd(observer, options, resolve), options.loadDeferredContentMaxIdleTime);
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
async function deferForceLazyLoadEnd(observer, options, resolve) {
|
|
174
174
|
await setAsyncTimeout("maxTimeout", async () => {
|
|
175
175
|
await clearAsyncTimeout("loadTimeout");
|
|
176
176
|
await lazyLoadEnd(observer, options, resolve);
|
|
177
|
-
}, options.
|
|
177
|
+
}, options.loadDeferredContentMaxIdleTime * 10);
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
async function lazyLoadEnd(observer, options, resolve) {
|
|
181
181
|
await clearAsyncTimeout("idleTimeout");
|
|
182
|
-
hooksFrames.
|
|
182
|
+
hooksFrames.loadDeferredContentEnd(options);
|
|
183
183
|
await setAsyncTimeout("endTimeout", async () => {
|
|
184
184
|
await clearAsyncTimeout("maxTimeout");
|
|
185
185
|
resolve();
|
|
186
|
-
}, options.
|
|
186
|
+
}, options.loadDeferredContentMaxIdleTime / 2);
|
|
187
187
|
observer.disconnect();
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
async function setAsyncTimeout(type, callback, delay
|
|
191
|
-
if (browser && browser.runtime && browser.runtime.sendMessage
|
|
190
|
+
async function setAsyncTimeout(type, callback, delay) {
|
|
191
|
+
if (browser && browser.runtime && browser.runtime.sendMessage) {
|
|
192
192
|
if (!timeouts.get(type) || !timeouts.get(type).pending) {
|
|
193
193
|
const timeoutData = { callback, pending: true };
|
|
194
194
|
timeouts.set(type, timeoutData);
|