single-file-core 1.6.28 → 1.6.29
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,9 @@ 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
|
+
return Promise.resolve({ options: session && session.options });
|
|
86
108
|
}
|
|
87
109
|
});
|
|
88
110
|
}
|
|
@@ -114,10 +136,15 @@ async function onMessage(event) {
|
|
|
114
136
|
}
|
|
115
137
|
if (!TOP_WINDOW) {
|
|
116
138
|
globalThis.stop();
|
|
117
|
-
if (message.options
|
|
118
|
-
|
|
139
|
+
if (!message.options && RUNTIME_MESSAGING) {
|
|
140
|
+
message.options = await getOptions(message.sessionId);
|
|
141
|
+
}
|
|
142
|
+
if (message.options) {
|
|
143
|
+
if (message.options.loadDeferredContent) {
|
|
144
|
+
lazy.process(message.options);
|
|
145
|
+
}
|
|
146
|
+
await initRequestAsync(message);
|
|
119
147
|
}
|
|
120
|
-
await initRequestAsync(message);
|
|
121
148
|
}
|
|
122
149
|
} else if (message.method == ACK_INIT_REQUEST_MESSAGE) {
|
|
123
150
|
clearFrameTimeout("requestTimeouts", message.sessionId, message.windowId);
|
|
@@ -133,12 +160,13 @@ async function onMessage(event) {
|
|
|
133
160
|
|
|
134
161
|
function getAsync(options) {
|
|
135
162
|
const sessionId = getNewSessionId();
|
|
136
|
-
options =
|
|
163
|
+
options = getFrameOptions(options);
|
|
137
164
|
return new Promise(resolve => {
|
|
138
165
|
sessions.set(sessionId, {
|
|
139
166
|
frames: [],
|
|
140
167
|
requestTimeouts: {},
|
|
141
168
|
responseTimeouts: {},
|
|
169
|
+
options,
|
|
142
170
|
resolve: frames => {
|
|
143
171
|
frames.sessionId = sessionId;
|
|
144
172
|
resolve(frames);
|
|
@@ -150,11 +178,12 @@ function getAsync(options) {
|
|
|
150
178
|
|
|
151
179
|
function getSync(options) {
|
|
152
180
|
const sessionId = getNewSessionId();
|
|
153
|
-
options =
|
|
181
|
+
options = getFrameOptions(options);
|
|
154
182
|
sessions.set(sessionId, {
|
|
155
183
|
frames: [],
|
|
156
184
|
requestTimeouts: {},
|
|
157
|
-
responseTimeouts: {}
|
|
185
|
+
responseTimeouts: {},
|
|
186
|
+
options
|
|
158
187
|
});
|
|
159
188
|
initRequestSync({ windowId, sessionId, options });
|
|
160
189
|
const frames = sessions.get(sessionId).frames;
|
|
@@ -171,6 +200,20 @@ function getNewSessionId() {
|
|
|
171
200
|
return globalThis.crypto.getRandomValues(new Uint32Array(32)).join("");
|
|
172
201
|
}
|
|
173
202
|
|
|
203
|
+
function getFrameOptions(options) {
|
|
204
|
+
return JSON.parse(JSON.stringify(options, FRAME_OPTION_NAMES));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function getOptions(sessionId) {
|
|
208
|
+
try {
|
|
209
|
+
const response = await browser.runtime.sendMessage({ method: GET_OPTIONS_MESSAGE, sessionId });
|
|
210
|
+
return response && response.options;
|
|
211
|
+
// eslint-disable-next-line no-unused-vars
|
|
212
|
+
} catch (error) {
|
|
213
|
+
// ignored
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
174
217
|
function initRequestSync(message) {
|
|
175
218
|
const sessionId = message.sessionId;
|
|
176
219
|
delete globalThis._singleFile_cleaningUp;
|
|
@@ -282,7 +325,7 @@ function processFramesAsync(doc, frameElements, options, parentWindowId, session
|
|
|
282
325
|
frameElements.forEach((frameElement, frameIndex) => {
|
|
283
326
|
const windowId = parentWindowId + WINDOW_ID_SEPARATOR + frameIndex;
|
|
284
327
|
try {
|
|
285
|
-
sendMessage(frameElement.contentWindow, { method: INIT_REQUEST_MESSAGE, windowId, sessionId, options, scrolling: frameElement.scrolling, waitForFrames });
|
|
328
|
+
sendMessage(frameElement.contentWindow, { method: INIT_REQUEST_MESSAGE, windowId, sessionId, options: RUNTIME_MESSAGING ? undefined : options, scrolling: frameElement.scrolling, waitForFrames });
|
|
286
329
|
// eslint-disable-next-line no-unused-vars
|
|
287
330
|
} catch (error) {
|
|
288
331
|
// 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 => {
|