single-file-core 1.6.28 → 1.6.30
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
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import * as cssUnescape from "./../vendor/css-unescape.js";
|
|
25
|
+
import * as srcsetParser from "./../vendor/html-srcset-parser.js";
|
|
25
26
|
import * as sha from "./lib/sha.js";
|
|
26
27
|
import * as hooksFrames from "./../processors/hooks/content/content-hooks-frames.js";
|
|
27
28
|
import * as infobar from "./infobar.js";
|
|
@@ -44,6 +45,7 @@ const ON_AFTER_CAPTURE_EVENT_NAME = SINGLE_FILE_PREFIX + "on-after-capture";
|
|
|
44
45
|
const GET_ADOPTED_STYLESHEETS_REQUEST_EVENT = SINGLE_FILE_PREFIX + "request-get-adopted-stylesheets";
|
|
45
46
|
const GET_ADOPTED_STYLESHEETS_RESPONSE_EVENT = SINGLE_FILE_PREFIX + "response-get-adopted-stylesheets";
|
|
46
47
|
const UNREGISTER_GET_ADOPTED_STYLESHEETS_REQUEST_EVENT = SINGLE_FILE_PREFIX + "unregister-request-get-adopted-stylesheets";
|
|
48
|
+
const CLEAR_ADOPTED_STYLESHEETS_CACHE_EVENT = SINGLE_FILE_PREFIX + "clear-adopted-stylesheets-cache";
|
|
47
49
|
const ON_INIT_USERSCRIPT_EVENT = SINGLE_FILE_PREFIX + "user-script-init";
|
|
48
50
|
const REMOVED_CONTENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "removed-content";
|
|
49
51
|
const HIDDEN_CONTENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "hidden-content";
|
|
@@ -117,6 +119,37 @@ const NESTING_SHADOW_ROOT_TRACK_ID_PREFIX = "s";
|
|
|
117
119
|
const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
|
|
118
120
|
const RAW_TEXT_TAG_NAMES = ["SCRIPT", "STYLE", "TEXTAREA", "TITLE", "XMP", "IFRAME", "NOEMBED", "NOFRAMES", "PLAINTEXT", "NOSCRIPT"];
|
|
119
121
|
const COMMENT_NODE_FILTER = 128;
|
|
122
|
+
const PROTECTED_OPTION_NAMES = [
|
|
123
|
+
"password",
|
|
124
|
+
"woleetKey",
|
|
125
|
+
"saveToGDrive",
|
|
126
|
+
"saveToDropbox",
|
|
127
|
+
"saveWithCompanion",
|
|
128
|
+
"forceWebAuthFlow",
|
|
129
|
+
"saveWithWebDAV",
|
|
130
|
+
"webDAVURL",
|
|
131
|
+
"webDAVUser",
|
|
132
|
+
"webDAVPassword",
|
|
133
|
+
"saveWithMCP",
|
|
134
|
+
"mcpServerUrl",
|
|
135
|
+
"mcpAuthToken",
|
|
136
|
+
"saveToGitHub",
|
|
137
|
+
"githubToken",
|
|
138
|
+
"githubUser",
|
|
139
|
+
"githubRepository",
|
|
140
|
+
"githubBranch",
|
|
141
|
+
"saveToRestFormApi",
|
|
142
|
+
"saveToRestFormApiUrl",
|
|
143
|
+
"saveToRestFormApiToken",
|
|
144
|
+
"saveToRestFormApiFileFieldName",
|
|
145
|
+
"saveToRestFormApiUrlFieldName",
|
|
146
|
+
"saveToS3",
|
|
147
|
+
"S3Domain",
|
|
148
|
+
"S3Region",
|
|
149
|
+
"S3Bucket",
|
|
150
|
+
"S3AccessKey",
|
|
151
|
+
"S3SecretKey"
|
|
152
|
+
];
|
|
120
153
|
const DEPRECATED_OPTION_NAMES = {
|
|
121
154
|
loadDeferredImages: "loadDeferredContent",
|
|
122
155
|
loadDeferredImagesMaxIdleTime: "loadDeferredContentMaxIdleTime",
|
|
@@ -260,7 +293,7 @@ function onInitUserScript({ detail }) {
|
|
|
260
293
|
delete userScriptOptions.frames;
|
|
261
294
|
delete userScriptOptions.taskId;
|
|
262
295
|
delete userScriptOptions._migratedTemplateFormat;
|
|
263
|
-
delete userScriptOptions
|
|
296
|
+
PROTECTED_OPTION_NAMES.forEach(name => delete userScriptOptions[name]);
|
|
264
297
|
let detailUserScript;
|
|
265
298
|
try {
|
|
266
299
|
detailUserScript = detail == "jsonDetail" ? JSON.stringify({ options: userScriptOptions }) : { options: userScriptOptions };
|
|
@@ -277,6 +310,7 @@ function onInitUserScript({ detail }) {
|
|
|
277
310
|
try {
|
|
278
311
|
const detail = typeof event.detail == "string" ? JSON.parse(event.detail) : event.detail;
|
|
279
312
|
if (detail.options) {
|
|
313
|
+
PROTECTED_OPTION_NAMES.forEach(name => delete detail.options[name]);
|
|
280
314
|
Object.assign(options, detail.options);
|
|
281
315
|
}
|
|
282
316
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -325,6 +359,12 @@ function preProcessDoc(doc, win, options) {
|
|
|
325
359
|
let elementsInfo;
|
|
326
360
|
if (win && doc.documentElement) {
|
|
327
361
|
markInvalidNesting(doc);
|
|
362
|
+
try {
|
|
363
|
+
doc.dispatchEvent(new CustomEvent(CLEAR_ADOPTED_STYLESHEETS_CACHE_EVENT));
|
|
364
|
+
// eslint-disable-next-line no-unused-vars
|
|
365
|
+
} catch (error) {
|
|
366
|
+
// ignored
|
|
367
|
+
}
|
|
328
368
|
elementsInfo = getElementsInfo(win, doc, doc.documentElement, options);
|
|
329
369
|
if (options.removeUnusedFonts && doc.defaultView) {
|
|
330
370
|
getRootElementUsedFonts(win, doc.documentElement, elementsInfo);
|
|
@@ -825,7 +865,7 @@ function getElementsInfo(win, doc, element, options, data = { usedFonts: new Map
|
|
|
825
865
|
shadowRootInfo.clonable = shadowRoot.clonable;
|
|
826
866
|
shadowRootInfo.serializable = shadowRoot.serializable;
|
|
827
867
|
try {
|
|
828
|
-
if (shadowRoot.adoptedStyleSheets
|
|
868
|
+
if (shadowRoot.adoptedStyleSheets) {
|
|
829
869
|
shadowRoot.dispatchEvent(new CustomEvent(UNREGISTER_GET_ADOPTED_STYLESHEETS_REQUEST_EVENT, { bubbles: true }));
|
|
830
870
|
}
|
|
831
871
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -909,6 +949,9 @@ function getResourcesInfo(win, doc, element, options, data, elementHidden, compu
|
|
|
909
949
|
EMPTY_RESOURCE :
|
|
910
950
|
(options.loadDeferredContent && element.getAttribute(LAZY_SRC_ATTRIBUTE_NAME)) || element.currentSrc
|
|
911
951
|
};
|
|
952
|
+
if (!elementHidden && element.parentElement && element.parentElement.tagName.toUpperCase() == "PICTURE") {
|
|
953
|
+
imageData.sourceSize = getSourceSize(win, element.parentElement, imageData.currentSrc);
|
|
954
|
+
}
|
|
912
955
|
data.images.push(imageData);
|
|
913
956
|
element.setAttribute(IMAGE_ATTRIBUTE_NAME, data.images.length - 1);
|
|
914
957
|
data.markedElements.push(element);
|
|
@@ -1242,6 +1285,42 @@ function getStylesheetsData(doc, markedElements) {
|
|
|
1242
1285
|
}
|
|
1243
1286
|
}
|
|
1244
1287
|
|
|
1288
|
+
function getSourceSize(win, pictureElement, currentSrc) {
|
|
1289
|
+
if (currentSrc) {
|
|
1290
|
+
const baseURI = pictureElement.ownerDocument.baseURI;
|
|
1291
|
+
const sourceElement = Array.from(pictureElement.querySelectorAll(":scope > source")).find(sourceElement => {
|
|
1292
|
+
const media = sourceElement.getAttribute("media");
|
|
1293
|
+
return (!media || win.matchMedia(media).matches) && getSrcsetURLs(sourceElement.getAttribute("srcset"), baseURI).includes(currentSrc);
|
|
1294
|
+
});
|
|
1295
|
+
if (sourceElement && (sourceElement.hasAttribute("width") || sourceElement.hasAttribute("height"))) {
|
|
1296
|
+
return {
|
|
1297
|
+
width: sourceElement.getAttribute("width"),
|
|
1298
|
+
height: sourceElement.getAttribute("height")
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
function getSrcsetURLs(srcset, baseURI) {
|
|
1305
|
+
const urls = [];
|
|
1306
|
+
if (srcset) {
|
|
1307
|
+
try {
|
|
1308
|
+
srcsetParser.process(srcset).forEach(candidate => {
|
|
1309
|
+
try {
|
|
1310
|
+
urls.push(new URL(candidate.url, baseURI).href);
|
|
1311
|
+
// eslint-disable-next-line no-unused-vars
|
|
1312
|
+
} catch (error) {
|
|
1313
|
+
// ignored
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
// eslint-disable-next-line no-unused-vars
|
|
1317
|
+
} catch (error) {
|
|
1318
|
+
// ignored
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
return urls;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1245
1324
|
function getSize(win, imageElement, computedStyle) {
|
|
1246
1325
|
let pxWidth = imageElement.naturalWidth;
|
|
1247
1326
|
let pxHeight = imageElement.naturalHeight;
|
package/core/index.js
CHANGED
|
@@ -735,6 +735,13 @@ class Processor {
|
|
|
735
735
|
if (imageData.currentSrc) {
|
|
736
736
|
imgElement.dataset.singleFileOriginURL = imgElement.getAttribute("src");
|
|
737
737
|
imgElement.setAttribute("src", imageData.currentSrc);
|
|
738
|
+
if (imageData.sourceSize && this.options.removeAlternativeImages) {
|
|
739
|
+
["width", "height"].forEach(attributeName => {
|
|
740
|
+
if (imageData.sourceSize[attributeName] != null) {
|
|
741
|
+
imgElement.setAttribute(attributeName, imageData.sourceSize[attributeName]);
|
|
742
|
+
}
|
|
743
|
+
});
|
|
744
|
+
}
|
|
738
745
|
}
|
|
739
746
|
if (this.options.loadDeferredContent) {
|
|
740
747
|
if ((!imgElement.getAttribute("src") || imgElement.getAttribute("src") == util.EMPTY_RESOURCE) && imgElement.getAttribute("data-src")) {
|
package/package.json
CHANGED
|
@@ -52,6 +52,24 @@ const INIT_REQUEST_MESSAGE = "singlefile.frameTree.initRequest";
|
|
|
52
52
|
const ACK_INIT_REQUEST_MESSAGE = "singlefile.frameTree.ackInitRequest";
|
|
53
53
|
const CLEANUP_REQUEST_MESSAGE = "singlefile.frameTree.cleanupRequest";
|
|
54
54
|
const INIT_RESPONSE_MESSAGE = "singlefile.frameTree.initResponse";
|
|
55
|
+
const GET_OPTIONS_MESSAGE = "singlefile.frameTree.getOptions";
|
|
56
|
+
const FRAME_OPTION_NAMES = [
|
|
57
|
+
"removeHiddenElements",
|
|
58
|
+
"removeUnusedFonts",
|
|
59
|
+
"compressHTML",
|
|
60
|
+
"autoSaveExternalSave",
|
|
61
|
+
"moveStylesInHead",
|
|
62
|
+
"blockVideos",
|
|
63
|
+
"customStylesheet",
|
|
64
|
+
"manifest",
|
|
65
|
+
"loadDeferredContent",
|
|
66
|
+
"loadDeferredContentMaxIdleTime",
|
|
67
|
+
"loadDeferredContentKeepZoomLevel",
|
|
68
|
+
"loadDeferredContentMinZoomFactor",
|
|
69
|
+
"loadDeferredContentDispatchScrollEvent",
|
|
70
|
+
"loadDeferredContentBlockCookies",
|
|
71
|
+
"loadDeferredContentBlockStorage"
|
|
72
|
+
];
|
|
55
73
|
const TARGET_ORIGIN = "*";
|
|
56
74
|
const TIMEOUT_INIT_REQUEST_MESSAGE = 5000;
|
|
57
75
|
const TIMEOUT_INIT_RESPONSE_MESSAGE = 10000;
|
|
@@ -60,6 +78,7 @@ const WINDOW_ID_SEPARATOR = ".";
|
|
|
60
78
|
const TOP_WINDOW = globalThis.window == globalThis.top;
|
|
61
79
|
|
|
62
80
|
const browser = globalThis.browser;
|
|
81
|
+
const RUNTIME_MESSAGING = Boolean(browser && browser.runtime && browser.runtime.sendMessage);
|
|
63
82
|
const top = globalThis.top;
|
|
64
83
|
const MessageChannel = globalThis.MessageChannel;
|
|
65
84
|
const document = globalThis.document;
|
|
@@ -83,6 +102,11 @@ if (TOP_WINDOW) {
|
|
|
83
102
|
clearFrameTimeout("requestTimeouts", message.sessionId, message.windowId);
|
|
84
103
|
createFrameResponseTimeout(message.sessionId, message.windowId);
|
|
85
104
|
return Promise.resolve({});
|
|
105
|
+
} else if (message.method == GET_OPTIONS_MESSAGE) {
|
|
106
|
+
const session = sessions.get(message.sessionId);
|
|
107
|
+
if (session) {
|
|
108
|
+
return Promise.resolve({ options: session.options });
|
|
109
|
+
}
|
|
86
110
|
}
|
|
87
111
|
});
|
|
88
112
|
}
|
|
@@ -114,10 +138,15 @@ async function onMessage(event) {
|
|
|
114
138
|
}
|
|
115
139
|
if (!TOP_WINDOW) {
|
|
116
140
|
globalThis.stop();
|
|
117
|
-
if (message.options
|
|
118
|
-
|
|
141
|
+
if (!message.options && RUNTIME_MESSAGING) {
|
|
142
|
+
message.options = await getOptions(message.sessionId);
|
|
143
|
+
}
|
|
144
|
+
if (message.options) {
|
|
145
|
+
if (message.options.loadDeferredContent) {
|
|
146
|
+
lazy.process(message.options);
|
|
147
|
+
}
|
|
148
|
+
await initRequestAsync(message);
|
|
119
149
|
}
|
|
120
|
-
await initRequestAsync(message);
|
|
121
150
|
}
|
|
122
151
|
} else if (message.method == ACK_INIT_REQUEST_MESSAGE) {
|
|
123
152
|
clearFrameTimeout("requestTimeouts", message.sessionId, message.windowId);
|
|
@@ -133,12 +162,13 @@ async function onMessage(event) {
|
|
|
133
162
|
|
|
134
163
|
function getAsync(options) {
|
|
135
164
|
const sessionId = getNewSessionId();
|
|
136
|
-
options =
|
|
165
|
+
options = getFrameOptions(options);
|
|
137
166
|
return new Promise(resolve => {
|
|
138
167
|
sessions.set(sessionId, {
|
|
139
168
|
frames: [],
|
|
140
169
|
requestTimeouts: {},
|
|
141
170
|
responseTimeouts: {},
|
|
171
|
+
options,
|
|
142
172
|
resolve: frames => {
|
|
143
173
|
frames.sessionId = sessionId;
|
|
144
174
|
resolve(frames);
|
|
@@ -150,11 +180,12 @@ function getAsync(options) {
|
|
|
150
180
|
|
|
151
181
|
function getSync(options) {
|
|
152
182
|
const sessionId = getNewSessionId();
|
|
153
|
-
options =
|
|
183
|
+
options = getFrameOptions(options);
|
|
154
184
|
sessions.set(sessionId, {
|
|
155
185
|
frames: [],
|
|
156
186
|
requestTimeouts: {},
|
|
157
|
-
responseTimeouts: {}
|
|
187
|
+
responseTimeouts: {},
|
|
188
|
+
options
|
|
158
189
|
});
|
|
159
190
|
initRequestSync({ windowId, sessionId, options });
|
|
160
191
|
const frames = sessions.get(sessionId).frames;
|
|
@@ -171,6 +202,20 @@ function getNewSessionId() {
|
|
|
171
202
|
return globalThis.crypto.getRandomValues(new Uint32Array(32)).join("");
|
|
172
203
|
}
|
|
173
204
|
|
|
205
|
+
function getFrameOptions(options) {
|
|
206
|
+
return JSON.parse(JSON.stringify(options, FRAME_OPTION_NAMES));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function getOptions(sessionId) {
|
|
210
|
+
try {
|
|
211
|
+
const response = await browser.runtime.sendMessage({ method: GET_OPTIONS_MESSAGE, sessionId });
|
|
212
|
+
return response && response.options;
|
|
213
|
+
// eslint-disable-next-line no-unused-vars
|
|
214
|
+
} catch (error) {
|
|
215
|
+
// ignored
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
174
219
|
function initRequestSync(message) {
|
|
175
220
|
const sessionId = message.sessionId;
|
|
176
221
|
delete globalThis._singleFile_cleaningUp;
|
|
@@ -282,7 +327,7 @@ function processFramesAsync(doc, frameElements, options, parentWindowId, session
|
|
|
282
327
|
frameElements.forEach((frameElement, frameIndex) => {
|
|
283
328
|
const windowId = parentWindowId + WINDOW_ID_SEPARATOR + frameIndex;
|
|
284
329
|
try {
|
|
285
|
-
sendMessage(frameElement.contentWindow, { method: INIT_REQUEST_MESSAGE, windowId, sessionId, options, scrolling: frameElement.scrolling, waitForFrames });
|
|
330
|
+
sendMessage(frameElement.contentWindow, { method: INIT_REQUEST_MESSAGE, windowId, sessionId, options: RUNTIME_MESSAGING ? undefined : options, scrolling: frameElement.scrolling, waitForFrames });
|
|
286
331
|
// eslint-disable-next-line no-unused-vars
|
|
287
332
|
} catch (error) {
|
|
288
333
|
// ignored
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
const GET_ADOPTED_STYLESHEETS_REQUEST_EVENT = "single-file-request-get-adopted-stylesheets";
|
|
46
46
|
const UNREGISTER_GET_ADOPTED_STYLESHEETS_REQUEST_EVENT = "single-file-unregister-request-get-adopted-stylesheets";
|
|
47
47
|
const GET_ADOPTED_STYLESHEETS_RESPONSE_EVENT = "single-file-response-get-adopted-stylesheets";
|
|
48
|
+
const CLEAR_ADOPTED_STYLESHEETS_CACHE_EVENT = "single-file-clear-adopted-stylesheets-cache";
|
|
48
49
|
const NEW_FONT_FACE_EVENT = "single-file-new-font-face";
|
|
49
50
|
const DELETE_FONT_EVENT = "single-file-delete-font";
|
|
50
51
|
const CLEAR_FONTS_EVENT = "single-file-clear-fonts";
|
|
@@ -97,6 +98,7 @@
|
|
|
97
98
|
|
|
98
99
|
let dispatchScrollEvent;
|
|
99
100
|
let adoptedStylesheetsData = new WeakMap();
|
|
101
|
+
let adoptedStylesheetsTexts = new WeakMap();
|
|
100
102
|
const shadowRootsData = new WeakMap();
|
|
101
103
|
|
|
102
104
|
init();
|
|
@@ -118,6 +120,7 @@
|
|
|
118
120
|
document.addEventListener(FETCH_SUPPORTED_REQUEST_EVENT, onFetchSupportedRequest);
|
|
119
121
|
document.addEventListener(FETCH_REQUEST_EVENT, onFetchRequest);
|
|
120
122
|
document.addEventListener(GET_ADOPTED_STYLESHEETS_REQUEST_EVENT, getAdoptedStylesheetsListener);
|
|
123
|
+
document.addEventListener(CLEAR_ADOPTED_STYLESHEETS_CACHE_EVENT, clearAdoptedStylesheetsCache);
|
|
121
124
|
document.addEventListener(BOOTSTRAP_EVENT, onBootstrap);
|
|
122
125
|
}
|
|
123
126
|
|
|
@@ -553,6 +556,7 @@
|
|
|
553
556
|
try {
|
|
554
557
|
const result = originalInsertRule.apply(this, [rule, arguments[1]]);
|
|
555
558
|
adoptedStylesheetsData.delete(this);
|
|
559
|
+
adoptedStylesheetsTexts.delete(this);
|
|
556
560
|
return result;
|
|
557
561
|
} catch (error) {
|
|
558
562
|
error.stack = error.message + "\n" + " \n" + error.stack.trim().split("\n").slice(-1).join("\n");
|
|
@@ -566,6 +570,7 @@
|
|
|
566
570
|
try {
|
|
567
571
|
const result = originalDeleteRule.apply(this, [index]);
|
|
568
572
|
adoptedStylesheetsData.delete(this);
|
|
573
|
+
adoptedStylesheetsTexts.delete(this);
|
|
569
574
|
return result;
|
|
570
575
|
} catch (error) {
|
|
571
576
|
error.stack = error.message + "\n" + " \n" + error.stack.trim().split("\n").slice(-1).join("\n");
|
|
@@ -600,9 +605,11 @@
|
|
|
600
605
|
const adoptedStyleSheets = Array.from(shadowRoot.adoptedStyleSheets).map(stylesheet => {
|
|
601
606
|
if (adoptedStylesheetsData.has(stylesheet)) {
|
|
602
607
|
return adoptedStylesheetsData.get(stylesheet);
|
|
608
|
+
} else if (adoptedStylesheetsTexts.has(stylesheet)) {
|
|
609
|
+
return adoptedStylesheetsTexts.get(stylesheet);
|
|
603
610
|
} else {
|
|
604
611
|
const text = Array.from(stylesheet.cssRules).map(cssRule => cssRule.cssText).join("\n");
|
|
605
|
-
|
|
612
|
+
adoptedStylesheetsTexts.set(stylesheet, text);
|
|
606
613
|
return text;
|
|
607
614
|
}
|
|
608
615
|
});
|
|
@@ -620,6 +627,10 @@
|
|
|
620
627
|
event.currentTarget.removeEventListener(GET_ADOPTED_STYLESHEETS_REQUEST_EVENT, getAdoptedStylesheetsListener, { capture: true });
|
|
621
628
|
}
|
|
622
629
|
|
|
630
|
+
function clearAdoptedStylesheetsCache() {
|
|
631
|
+
adoptedStylesheetsTexts = new WeakMap();
|
|
632
|
+
}
|
|
633
|
+
|
|
623
634
|
async function getDetailObject(fontFace, src) {
|
|
624
635
|
const detail = {};
|
|
625
636
|
Object.keys(FONT_STYLE_PROPERTIES).forEach(descriptor => {
|