single-file-cli 1.1.53 → 2.0.2
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/{.eslintrc.js → .eslintrc.json} +5 -6
- package/Dockerfile +2 -1
- package/README.MD +56 -60
- package/args.js +252 -334
- package/back-ends/chromium-back-end.js +223 -0
- package/back-ends/chromium-browser.js +89 -0
- package/back-ends/chromium-constants.js +157 -0
- package/back-ends/{common/scripts.js → single-file-script.js} +21 -49
- package/build-lib-dev.sh +84 -0
- package/build-lib.sh +80 -3
- package/compile.sh +13 -0
- package/deno-polyfill.js +223 -0
- package/deno.json +13 -0
- package/deno.lock +49 -0
- package/lib/single-file-bundle.js +1 -0
- package/package.json +9 -33
- package/single-file +39 -14
- package/single-file-cli-api.js +32 -44
- package/back-ends/extensions/bypass-csp/index.js +0 -36
- package/back-ends/extensions/bypass-csp/manifest.json +0 -20
- package/back-ends/extensions/disable-web-security/index.js +0 -54
- package/back-ends/extensions/disable-web-security/manifest.json +0 -20
- package/back-ends/extensions/network-idle/bg.js +0 -61
- package/back-ends/extensions/network-idle/content.js +0 -29
- package/back-ends/extensions/network-idle/manifest.json +0 -31
- package/back-ends/extensions/signed/bypass_csp-0.0.3-an+fx.xpi +0 -0
- package/back-ends/extensions/signed/disable_web_security-0.0.3-an+fx.xpi +0 -0
- package/back-ends/extensions/signed/network_idle-0.0.2-an+fx.xpi +0 -0
- package/back-ends/jsdom.js +0 -159
- package/back-ends/playwright-chromium.js +0 -126
- package/back-ends/playwright-firefox.js +0 -115
- package/back-ends/playwright-webkit.js +0 -126
- package/back-ends/puppeteer-firefox.js +0 -172
- package/back-ends/puppeteer.js +0 -201
- package/back-ends/webdriver-chromium.js +0 -172
- package/back-ends/webdriver-gecko.js +0 -190
- package/lib/single-file-bootstrap.js +0 -1
- package/lib/single-file-frames.js +0 -1
- package/lib/single-file-hooks-frames.js +0 -1
- package/lib/single-file-zip.min.js +0 -1
- package/lib/single-file.js +0 -1
- package/rollup.config.dev.js +0 -57
- package/rollup.config.js +0 -58
package/package.json
CHANGED
|
@@ -1,41 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "single-file-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "SingleFile CLI",
|
|
5
5
|
"author": "Gildas Lormeau",
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
6
|
+
"engines": {
|
|
7
|
+
"deno": ">=1.4",
|
|
8
|
+
"bun": ">=1.0",
|
|
9
|
+
"node": ">=20"
|
|
10
10
|
},
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
"prepublishOnly": "./build-lib.sh"
|
|
14
|
-
},
|
|
15
|
-
"repository": {
|
|
16
|
-
"type": "git",
|
|
17
|
-
"url": "git+https://github.com/gildas-lormeau/single-file-cli.git"
|
|
18
|
-
},
|
|
19
|
-
"bugs": {
|
|
20
|
-
"url": "https://github.com/gildas-lormeau/single-file-cli/issues"
|
|
21
|
-
},
|
|
22
|
-
"homepage": "https://github.com/gildas-lormeau/single-file-cli#readme",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"bin": "./single-file",
|
|
23
13
|
"dependencies": {
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"jsdom": "24.0.0",
|
|
27
|
-
"puppeteer-core": "22.0.0",
|
|
28
|
-
"selenium-webdriver": "4.17.0",
|
|
29
|
-
"single-file-core": "1.3.23",
|
|
30
|
-
"strong-data-uri": "1.0.6",
|
|
31
|
-
"yargs": "17.7.2"
|
|
32
|
-
},
|
|
33
|
-
"devDependencies": {
|
|
34
|
-
"@rollup/plugin-node-resolve": "15.0.1",
|
|
35
|
-
"rollup": "2.79.1",
|
|
36
|
-
"rollup-plugin-terser": "7.0.2"
|
|
37
|
-
},
|
|
38
|
-
"overrides": {
|
|
39
|
-
"terser": "^5.15.0"
|
|
14
|
+
"simple-cdp": "^1.8.2",
|
|
15
|
+
"ws": "^8.16.0"
|
|
40
16
|
}
|
|
41
17
|
}
|
package/single-file
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env -S deno run --allow-read --allow-write --allow-net --allow-env --allow-run
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
|
-
* Copyright 2010-
|
|
4
|
+
* Copyright 2010-2024 Gildas Lormeau
|
|
5
5
|
* contact : gildas.lormeau <at> gmail.com
|
|
6
6
|
*
|
|
7
7
|
* This file is part of SingleFile.
|
|
@@ -23,34 +23,59 @@
|
|
|
23
23
|
* Source.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
import { VALID_URL_TEST, initialize } from "./single-file-cli-api.js";
|
|
27
|
+
import { initGlobalThisProperties, readTextFile, toFileUrl } from "./deno-polyfill.js";
|
|
28
|
+
import args from "./args.js";
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const api = require("./single-file-cli-api");
|
|
31
|
-
run(require("./args"))
|
|
32
|
-
.catch(error => console.error(error.message || error)); // eslint-disable-line no-console
|
|
30
|
+
await initGlobalThisProperties();
|
|
31
|
+
await run(args);
|
|
33
32
|
|
|
34
|
-
async function run(options) {
|
|
33
|
+
async function run(options = {}) {
|
|
35
34
|
let urls;
|
|
36
|
-
if (options.url && !
|
|
37
|
-
options.url =
|
|
35
|
+
if (options.url && !VALID_URL_TEST.test(options.url)) {
|
|
36
|
+
options.url = (await toFileUrl(new URL(options.url, import.meta.url).pathname)).href;
|
|
38
37
|
}
|
|
39
38
|
if (options.urlsFile) {
|
|
40
|
-
urls =
|
|
39
|
+
urls = (await readTextFile(options.urlsFile)).split("\n");
|
|
41
40
|
} else {
|
|
42
41
|
urls = [options.url];
|
|
43
42
|
}
|
|
43
|
+
if (options.browserCookies) {
|
|
44
|
+
const cookies = [];
|
|
45
|
+
for (const cookie of options.browserCookies) {
|
|
46
|
+
const [name, value, domain, path, expires, httpOnly, secure, sameSite, url] = cookie.split(",");
|
|
47
|
+
cookies.push({
|
|
48
|
+
name,
|
|
49
|
+
value,
|
|
50
|
+
url,
|
|
51
|
+
domain,
|
|
52
|
+
path,
|
|
53
|
+
secure,
|
|
54
|
+
httpOnly,
|
|
55
|
+
sameSite,
|
|
56
|
+
expires
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
options.browserCookies = cookies;
|
|
60
|
+
}
|
|
44
61
|
if (options.browserCookiesFile) {
|
|
45
|
-
const cookiesContent =
|
|
62
|
+
const cookiesContent = await readTextFile(options.browserCookiesFile);
|
|
46
63
|
try {
|
|
47
64
|
options.browserCookies = JSON.parse(cookiesContent);
|
|
48
65
|
} catch (error) {
|
|
49
66
|
options.browserCookies = parseCookies(cookiesContent);
|
|
50
67
|
}
|
|
51
68
|
}
|
|
69
|
+
if (options.httpHeaders) {
|
|
70
|
+
const headers = {};
|
|
71
|
+
for (const header of options.httpHeaders) {
|
|
72
|
+
const [name, value] = header.split("=");
|
|
73
|
+
headers[name] = value.trim();
|
|
74
|
+
}
|
|
75
|
+
options.httpHeaders = headers;
|
|
76
|
+
}
|
|
52
77
|
options.retrieveLinks = true;
|
|
53
|
-
const singlefile = await
|
|
78
|
+
const singlefile = await initialize(options);
|
|
54
79
|
await singlefile.capture(urls);
|
|
55
80
|
await singlefile.finish();
|
|
56
81
|
}
|
package/single-file-cli-api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright 2010-
|
|
2
|
+
* Copyright 2010-2024 Gildas Lormeau
|
|
3
3
|
* contact : gildas.lormeau <at> gmail.com
|
|
4
4
|
*
|
|
5
5
|
* This file is part of SingleFile.
|
|
@@ -21,10 +21,12 @@
|
|
|
21
21
|
* Source.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
/* global
|
|
24
|
+
/* global URL */
|
|
25
|
+
|
|
26
|
+
import * as backend from "./back-ends/chromium-back-end.js";
|
|
27
|
+
import { getZipScriptSource } from "./back-ends/single-file-script.js";
|
|
28
|
+
import { readTextFile, writeTextFile, stdout, mkdir, stat, dirname } from "./deno-polyfill.js";
|
|
25
29
|
|
|
26
|
-
const fs = require("fs");
|
|
27
|
-
const path = require("path");
|
|
28
30
|
const VALID_URL_TEST = /^(https?|file):\/\//;
|
|
29
31
|
|
|
30
32
|
const DEFAULT_OPTIONS = {
|
|
@@ -74,32 +76,17 @@ const DEFAULT_OPTIONS = {
|
|
|
74
76
|
const STATE_PROCESSING = "processing";
|
|
75
77
|
const STATE_PROCESSED = "processed";
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
jsdom: "./back-ends/jsdom.js",
|
|
79
|
-
puppeteer: "./back-ends/puppeteer.js",
|
|
80
|
-
"puppeteer-firefox": "./back-ends/puppeteer-firefox.js",
|
|
81
|
-
"webdriver-chromium": "./back-ends/webdriver-chromium.js",
|
|
82
|
-
"webdriver-gecko": "./back-ends/webdriver-gecko.js",
|
|
83
|
-
"playwright-firefox": "./back-ends/playwright-firefox.js",
|
|
84
|
-
"playwright-chromium": "./back-ends/playwright-chromium.js",
|
|
85
|
-
"playwright-webkit": "./back-ends/playwright-webkit.js"
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
let backend, tasks = [], maxParallelWorkers = 8, sessionFilename;
|
|
79
|
+
let tasks = [], maxParallelWorkers, sessionFilename;
|
|
89
80
|
|
|
90
|
-
|
|
91
|
-
exports.DEFAULT_OPTIONS = DEFAULT_OPTIONS;
|
|
92
|
-
exports.VALID_URL_TEST = VALID_URL_TEST;
|
|
93
|
-
exports.initialize = initialize;
|
|
81
|
+
export { DEFAULT_OPTIONS, VALID_URL_TEST, initialize };
|
|
94
82
|
|
|
95
83
|
async function initialize(options) {
|
|
96
84
|
options = Object.assign({}, DEFAULT_OPTIONS, options);
|
|
97
|
-
maxParallelWorkers = options.maxParallelWorkers;
|
|
98
|
-
backend = require(backEnds[options.backEnd]);
|
|
85
|
+
maxParallelWorkers = options.maxParallelWorkers || 8;
|
|
99
86
|
await backend.initialize(options);
|
|
100
87
|
if (options.crawlSyncSession || options.crawlLoadSession) {
|
|
101
88
|
try {
|
|
102
|
-
tasks = JSON.parse(
|
|
89
|
+
tasks = JSON.parse(await readTextFile(options.crawlSyncSession || options.crawlLoadSession));
|
|
103
90
|
} catch (error) {
|
|
104
91
|
if (options.crawlLoadSession) {
|
|
105
92
|
throw error;
|
|
@@ -122,7 +109,7 @@ async function capture(urls, options) {
|
|
|
122
109
|
newTasks = newTasks.filter(task => task && !taskUrls.includes(task.url));
|
|
123
110
|
if (newTasks.length) {
|
|
124
111
|
tasks = tasks.concat(newTasks);
|
|
125
|
-
saveTasks();
|
|
112
|
+
await saveTasks();
|
|
126
113
|
}
|
|
127
114
|
await runTasks();
|
|
128
115
|
}
|
|
@@ -131,9 +118,9 @@ async function finish(options) {
|
|
|
131
118
|
const promiseTasks = tasks.map(task => task.promise);
|
|
132
119
|
await Promise.all(promiseTasks);
|
|
133
120
|
if (options.crawlReplaceURLs && !options.compressContent) {
|
|
134
|
-
|
|
121
|
+
for (const task of tasks) {
|
|
135
122
|
try {
|
|
136
|
-
let pageContent =
|
|
123
|
+
let pageContent = await readTextFile(task.filename);
|
|
137
124
|
tasks.forEach(otherTask => {
|
|
138
125
|
if (otherTask.filename) {
|
|
139
126
|
pageContent = pageContent.replace(new RegExp(escapeRegExp("\"" + otherTask.originalUrl + "\""), "gi"), "\"" + otherTask.filename + "\"");
|
|
@@ -143,11 +130,11 @@ async function finish(options) {
|
|
|
143
130
|
pageContent = pageContent.replace(new RegExp(escapeRegExp("=" + otherTask.originalUrl + ">"), "gi"), "=" + filename + ">");
|
|
144
131
|
}
|
|
145
132
|
});
|
|
146
|
-
|
|
133
|
+
await writeTextFile(task.filename, pageContent);
|
|
147
134
|
} catch (error) {
|
|
148
135
|
// ignored
|
|
149
136
|
}
|
|
150
|
-
}
|
|
137
|
+
}
|
|
151
138
|
}
|
|
152
139
|
if (!options.browserDebug) {
|
|
153
140
|
return backend.closeBrowser();
|
|
@@ -171,7 +158,7 @@ async function runNextTask() {
|
|
|
171
158
|
let taskOptions = JSON.parse(JSON.stringify(options));
|
|
172
159
|
taskOptions.url = task.url;
|
|
173
160
|
task.status = STATE_PROCESSING;
|
|
174
|
-
saveTasks();
|
|
161
|
+
await saveTasks();
|
|
175
162
|
task.promise = capturePage(taskOptions);
|
|
176
163
|
const pageData = await task.promise;
|
|
177
164
|
task.status = STATE_PROCESSED;
|
|
@@ -188,7 +175,7 @@ async function runNextTask() {
|
|
|
188
175
|
tasks.splice(tasks.length, 0, ...newTasks);
|
|
189
176
|
}
|
|
190
177
|
}
|
|
191
|
-
saveTasks();
|
|
178
|
+
await saveTasks();
|
|
192
179
|
await runTasks();
|
|
193
180
|
}
|
|
194
181
|
}
|
|
@@ -218,9 +205,9 @@ function createTask(url, options, parentTask, rootTask) {
|
|
|
218
205
|
}
|
|
219
206
|
}
|
|
220
207
|
|
|
221
|
-
function saveTasks() {
|
|
208
|
+
async function saveTasks() {
|
|
222
209
|
if (sessionFilename) {
|
|
223
|
-
|
|
210
|
+
await writeTextFile(sessionFilename, JSON.stringify(
|
|
224
211
|
tasks.map(task => Object.assign({}, task, {
|
|
225
212
|
status: task.status == STATE_PROCESSING ? undefined : task.status,
|
|
226
213
|
promise: undefined,
|
|
@@ -252,38 +239,38 @@ function getHostURL(url) {
|
|
|
252
239
|
async function capturePage(options) {
|
|
253
240
|
try {
|
|
254
241
|
let filename;
|
|
255
|
-
options.zipScript =
|
|
242
|
+
options.zipScript = getZipScriptSource();
|
|
256
243
|
const pageData = await backend.getPageData(options);
|
|
257
244
|
if (options.output) {
|
|
258
|
-
filename = getFilename(options.output, options);
|
|
245
|
+
filename = await getFilename(options.output, options);
|
|
259
246
|
} else if (options.dumpContent) {
|
|
260
247
|
if (options.compressContent) {
|
|
261
|
-
await
|
|
248
|
+
await stdout.write(pageData.content);
|
|
262
249
|
} else {
|
|
263
250
|
console.log(pageData.content); // eslint-disable-line no-console
|
|
264
251
|
}
|
|
265
252
|
} else {
|
|
266
|
-
filename = getFilename(pageData.filename, options);
|
|
253
|
+
filename = await getFilename(pageData.filename, options);
|
|
267
254
|
}
|
|
268
255
|
if (filename) {
|
|
269
|
-
const
|
|
270
|
-
if (
|
|
271
|
-
|
|
256
|
+
const directoryName = await dirname(filename);
|
|
257
|
+
if (directoryName !== ".") {
|
|
258
|
+
await mkdir(directoryName, { recursive: true });
|
|
272
259
|
}
|
|
273
|
-
|
|
260
|
+
await writeTextFile(filename, pageData.content);
|
|
274
261
|
}
|
|
275
262
|
return pageData;
|
|
276
263
|
} catch (error) {
|
|
277
264
|
const message = "URL: " + options.url + "\nStack: " + error.stack + "\n";
|
|
278
265
|
if (options.errorFile) {
|
|
279
|
-
|
|
266
|
+
await writeTextFile(options.errorFile, message, { append: true });
|
|
280
267
|
} else {
|
|
281
268
|
console.error(error.message || error, message); // eslint-disable-line no-console
|
|
282
269
|
}
|
|
283
270
|
}
|
|
284
271
|
}
|
|
285
272
|
|
|
286
|
-
function getFilename(filename, options, index = 1) {
|
|
273
|
+
async function getFilename(filename, options, index = 1) {
|
|
287
274
|
if (Array.isArray(options.outputDirectory)) {
|
|
288
275
|
const outputDirectory = options.outputDirectory.pop();
|
|
289
276
|
if (outputDirectory.startsWith("/")) {
|
|
@@ -308,11 +295,12 @@ function getFilename(filename, options, index = 1) {
|
|
|
308
295
|
newFilename += " (" + index + ")";
|
|
309
296
|
}
|
|
310
297
|
}
|
|
311
|
-
|
|
298
|
+
try {
|
|
299
|
+
await stat(newFilename);
|
|
312
300
|
if (options.filenameConflictAction != "skip") {
|
|
313
301
|
return getFilename(filename, options, index + 1);
|
|
314
302
|
}
|
|
315
|
-
}
|
|
303
|
+
} catch (_) {
|
|
316
304
|
return newFilename;
|
|
317
305
|
}
|
|
318
306
|
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2010-2020 Gildas Lormeau
|
|
3
|
-
* contact : gildas.lormeau <at> gmail.com
|
|
4
|
-
*
|
|
5
|
-
* This file is part of SingleFile.
|
|
6
|
-
*
|
|
7
|
-
* The code in this file is free software: you can redistribute it and/or
|
|
8
|
-
* modify it under the terms of the GNU Affero General Public License
|
|
9
|
-
* (GNU AGPL) as published by the Free Software Foundation, either version 3
|
|
10
|
-
* of the License, or (at your option) any later version.
|
|
11
|
-
*
|
|
12
|
-
* The code in this file is distributed in the hope that it will be useful,
|
|
13
|
-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
|
|
15
|
-
* General Public License for more details.
|
|
16
|
-
*
|
|
17
|
-
* As additional permission under GNU AGPL version 3 section 7, you may
|
|
18
|
-
* distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
|
|
19
|
-
* AGPL normally required by section 4, provided you include this license
|
|
20
|
-
* notice and a URL through which recipients can access the Corresponding
|
|
21
|
-
* Source.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
const removedHeaders = ["content-security-policy"];
|
|
25
|
-
|
|
26
|
-
const browserAPI = this.browser || this.chrome;
|
|
27
|
-
|
|
28
|
-
browserAPI.webRequest.onHeadersReceived.addListener(
|
|
29
|
-
function (details) {
|
|
30
|
-
let responseHeaders = details.responseHeaders;
|
|
31
|
-
responseHeaders = responseHeaders.filter(responseHeader => !removedHeaders.includes(responseHeader.name.toLowerCase()));
|
|
32
|
-
return { responseHeaders };
|
|
33
|
-
},
|
|
34
|
-
{ urls: ["<all_urls>"] },
|
|
35
|
-
["blocking", "responseHeaders"]
|
|
36
|
-
);
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "bypass-csp",
|
|
3
|
-
"version": "0.0.3",
|
|
4
|
-
"background": {
|
|
5
|
-
"scripts": [
|
|
6
|
-
"index.js"
|
|
7
|
-
]
|
|
8
|
-
},
|
|
9
|
-
"permissions": [
|
|
10
|
-
"webRequest",
|
|
11
|
-
"webRequestBlocking",
|
|
12
|
-
"<all_urls>"
|
|
13
|
-
],
|
|
14
|
-
"browser_specific_settings": {
|
|
15
|
-
"gecko": {
|
|
16
|
-
"id": "{55e2789c-817b-4d75-815b-df6921c84ed8}"
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
"manifest_version": 2
|
|
20
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2010-2020 Gildas Lormeau
|
|
3
|
-
* contact : gildas.lormeau <at> gmail.com
|
|
4
|
-
*
|
|
5
|
-
* This file is part of SingleFile.
|
|
6
|
-
*
|
|
7
|
-
* The code in this file is free software: you can redistribute it and/or
|
|
8
|
-
* modify it under the terms of the GNU Affero General Public License
|
|
9
|
-
* (GNU AGPL) as published by the Free Software Foundation, either version 3
|
|
10
|
-
* of the License, or (at your option) any later version.
|
|
11
|
-
*
|
|
12
|
-
* The code in this file is distributed in the hope that it will be useful,
|
|
13
|
-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
|
|
15
|
-
* General Public License for more details.
|
|
16
|
-
*
|
|
17
|
-
* As additional permission under GNU AGPL version 3 section 7, you may
|
|
18
|
-
* distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
|
|
19
|
-
* AGPL normally required by section 4, provided you include this license
|
|
20
|
-
* notice and a URL through which recipients can access the Corresponding
|
|
21
|
-
* Source.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
const removedHeaders = ["access-control-allow-methods", "access-control-allow-headers"];
|
|
25
|
-
const updatedHeaders = { "access-control-allow-origin": "*", "access-control-allow-credentials": "true" };
|
|
26
|
-
|
|
27
|
-
const browserAPI = this.browser || this.chrome;
|
|
28
|
-
|
|
29
|
-
browserAPI.webRequest.onHeadersReceived.addListener(
|
|
30
|
-
function (details) {
|
|
31
|
-
let responseHeaders = details.responseHeaders;
|
|
32
|
-
let processedHeaders = [];
|
|
33
|
-
responseHeaders = responseHeaders.filter(responseHeader => !removedHeaders.includes(responseHeader.name.toLowerCase()));
|
|
34
|
-
responseHeaders.forEach(responseHeader => {
|
|
35
|
-
const name = responseHeader.name.toLowerCase();
|
|
36
|
-
const value = updatedHeaders[name];
|
|
37
|
-
if (value) {
|
|
38
|
-
responseHeader.value = value;
|
|
39
|
-
processedHeaders.push(name);
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
Object.keys(updatedHeaders).forEach(name => {
|
|
43
|
-
if (!processedHeaders.includes(name)) {
|
|
44
|
-
const value = updatedHeaders[name.toLowerCase()];
|
|
45
|
-
if (value) {
|
|
46
|
-
responseHeaders.push({ name, value });
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
return { responseHeaders };
|
|
51
|
-
},
|
|
52
|
-
{ urls: ["<all_urls>"] },
|
|
53
|
-
["blocking", "responseHeaders"]
|
|
54
|
-
);
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "disable-web-security",
|
|
3
|
-
"version": "0.0.3",
|
|
4
|
-
"background": {
|
|
5
|
-
"scripts": [
|
|
6
|
-
"index.js"
|
|
7
|
-
]
|
|
8
|
-
},
|
|
9
|
-
"permissions": [
|
|
10
|
-
"webRequest",
|
|
11
|
-
"webRequestBlocking",
|
|
12
|
-
"<all_urls>"
|
|
13
|
-
],
|
|
14
|
-
"browser_specific_settings": {
|
|
15
|
-
"gecko": {
|
|
16
|
-
"id": "{588434c2-67c3-4f77-9828-c30c7d63e8f9}"
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
"manifest_version": 2
|
|
20
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2010-2020 Gildas Lormeau
|
|
3
|
-
* contact : gildas.lormeau <at> gmail.com
|
|
4
|
-
*
|
|
5
|
-
* This file is part of SingleFile.
|
|
6
|
-
*
|
|
7
|
-
* The code in this file is free software: you can redistribute it and/or
|
|
8
|
-
* modify it under the terms of the GNU Affero General Public License
|
|
9
|
-
* (GNU AGPL) as published by the Free Software Foundation, either version 3
|
|
10
|
-
* of the License, or (at your option) any later version.
|
|
11
|
-
*
|
|
12
|
-
* The code in this file is distributed in the hope that it will be useful,
|
|
13
|
-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
|
|
15
|
-
* General Public License for more details.
|
|
16
|
-
*
|
|
17
|
-
* As additional permission under GNU AGPL version 3 section 7, you may
|
|
18
|
-
* distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
|
|
19
|
-
* AGPL normally required by section 4, provided you include this license
|
|
20
|
-
* notice and a URL through which recipients can access the Corresponding
|
|
21
|
-
* Source.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
/* global setTimeout, clearTimeout */
|
|
25
|
-
|
|
26
|
-
const browserAPI = this.browser || this.chrome;
|
|
27
|
-
|
|
28
|
-
const IDLE_DELAY = 1000;
|
|
29
|
-
const watchDogs = [];
|
|
30
|
-
let pendingRequests = new Set();
|
|
31
|
-
|
|
32
|
-
browserAPI.webRequest.onSendHeaders.addListener(onRequest, { urls: ["<all_urls>"] }, []);
|
|
33
|
-
browserAPI.webRequest.onResponseStarted.addListener(onResponse, { urls: ["<all_urls>"] }, []);
|
|
34
|
-
browserAPI.webRequest.onErrorOccurred.addListener(onResponse, { urls: ["<all_urls>"] });
|
|
35
|
-
|
|
36
|
-
function onRequest(details) {
|
|
37
|
-
if (details.tabId != -1) {
|
|
38
|
-
pendingRequests.add(details.requestId);
|
|
39
|
-
if (pendingRequests.size > 2) {
|
|
40
|
-
clearTimeout(watchDogs[2]);
|
|
41
|
-
}
|
|
42
|
-
clearTimeout(watchDogs[0]);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function onResponse(details) {
|
|
47
|
-
if (details.tabId != -1) {
|
|
48
|
-
pendingRequests.delete(details.requestId);
|
|
49
|
-
if (pendingRequests.size == 2) {
|
|
50
|
-
maybeIdle(2, details.tabId);
|
|
51
|
-
}
|
|
52
|
-
if (pendingRequests.size == 0) {
|
|
53
|
-
maybeIdle(0, details.tabId);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function maybeIdle(idleLevel, tabId) {
|
|
59
|
-
clearTimeout(watchDogs[idleLevel]);
|
|
60
|
-
watchDogs[idleLevel] = setTimeout(() => browserAPI.tabs.sendMessage(tabId, "network-idle-" + idleLevel), IDLE_DELAY);
|
|
61
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2010-2020 Gildas Lormeau
|
|
3
|
-
* contact : gildas.lormeau <at> gmail.com
|
|
4
|
-
*
|
|
5
|
-
* This file is part of SingleFile.
|
|
6
|
-
*
|
|
7
|
-
* The code in this file is free software: you can redistribute it and/or
|
|
8
|
-
* modify it under the terms of the GNU Affero General Public License
|
|
9
|
-
* (GNU AGPL) as published by the Free Software Foundation, either version 3
|
|
10
|
-
* of the License, or (at your option) any later version.
|
|
11
|
-
*
|
|
12
|
-
* The code in this file is distributed in the hope that it will be useful,
|
|
13
|
-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
|
|
15
|
-
* General Public License for more details.
|
|
16
|
-
*
|
|
17
|
-
* As additional permission under GNU AGPL version 3 section 7, you may
|
|
18
|
-
* distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
|
|
19
|
-
* AGPL normally required by section 4, provided you include this license
|
|
20
|
-
* notice and a URL through which recipients can access the Corresponding
|
|
21
|
-
* Source.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
/* global window, CustomEvent */
|
|
25
|
-
|
|
26
|
-
const browserAPI = this.browser || this.chrome;
|
|
27
|
-
const dispatchEvent = event => { try { window.dispatchEvent(event); } catch (error) { /* ignored */ } };
|
|
28
|
-
|
|
29
|
-
browserAPI.runtime.onMessage.addListener(message => dispatchEvent(new CustomEvent("single-file-" + message)));
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "network-idle",
|
|
3
|
-
"version": "0.0.2",
|
|
4
|
-
"background": {
|
|
5
|
-
"scripts": [
|
|
6
|
-
"bg.js"
|
|
7
|
-
]
|
|
8
|
-
},
|
|
9
|
-
"content_scripts": [
|
|
10
|
-
{
|
|
11
|
-
"matches": [
|
|
12
|
-
"<all_urls>"
|
|
13
|
-
],
|
|
14
|
-
"run_at": "document_start",
|
|
15
|
-
"js": [
|
|
16
|
-
"content.js"
|
|
17
|
-
]
|
|
18
|
-
}
|
|
19
|
-
],
|
|
20
|
-
"permissions": [
|
|
21
|
-
"tabs",
|
|
22
|
-
"webRequest",
|
|
23
|
-
"<all_urls>"
|
|
24
|
-
],
|
|
25
|
-
"browser_specific_settings": {
|
|
26
|
-
"gecko": {
|
|
27
|
-
"id": "{caf9306a-8951-4f0c-beb0-bab690d00caf}"
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
"manifest_version": 2
|
|
31
|
-
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|