single-file-core 1.0.13 → 1.0.16
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/modules/css-fonts-minifier.js +1 -1
- package/package.json +1 -1
- package/processors/frame-tree/content/content-frame-tree.js +0 -2
- package/processors/hooks/content/content-hooks-frames.js +0 -88
- package/processors/index.js +0 -2
- package/single-file-core.js +3 -77
- package/{processors/hooks/content/content-hooks.js → single-file-frames.js} +1 -16
- package/single-file-helper.js +2 -14
- package/single-file-hooks-frames.js +24 -0
- package/single-file-hooks.js +24 -0
- package/single-file-infobar.js +24 -0
- package/single-file-util.js +0 -1
- package/{index.js → single-file.js} +0 -0
- package/vendor/css-font-property-parser.js +27 -160
|
@@ -244,7 +244,7 @@ function getFontFamilyNames(declarations) {
|
|
|
244
244
|
const font = declarations.children.filter(node => node.property == "font").tail;
|
|
245
245
|
if (font && font.data && font.data.value) {
|
|
246
246
|
try {
|
|
247
|
-
const parsedFont = fontPropertyParser.parse(
|
|
247
|
+
const parsedFont = fontPropertyParser.parse(font.data.value);
|
|
248
248
|
parsedFont.family.forEach(familyName => fontFamilyNames.push(helper.normalizeFontFamily(familyName)));
|
|
249
249
|
} catch (error) {
|
|
250
250
|
// ignored
|
package/package.json
CHANGED
|
@@ -233,7 +233,6 @@ function initResponse(message) {
|
|
|
233
233
|
frameData.videos = messageFrameData.videos;
|
|
234
234
|
frameData.usedFonts = messageFrameData.usedFonts;
|
|
235
235
|
frameData.shadowRoots = messageFrameData.shadowRoots;
|
|
236
|
-
frameData.imports = messageFrameData.imports;
|
|
237
236
|
frameData.processed = messageFrameData.processed;
|
|
238
237
|
}
|
|
239
238
|
});
|
|
@@ -401,7 +400,6 @@ function getFrameData(document, globalThis, windowId, options) {
|
|
|
401
400
|
videos: docData.videos,
|
|
402
401
|
usedFonts: docData.usedFonts,
|
|
403
402
|
shadowRoots: docData.shadowRoots,
|
|
404
|
-
imports: docData.imports,
|
|
405
403
|
processed: true
|
|
406
404
|
};
|
|
407
405
|
}
|
|
@@ -41,7 +41,6 @@ const NEW_FONT_FACE_EVENT = "single-file-new-font-face";
|
|
|
41
41
|
const DELETE_FONT_EVENT = "single-file-delete-font";
|
|
42
42
|
const CLEAR_FONTS_EVENT = "single-file-clear-fonts";
|
|
43
43
|
|
|
44
|
-
const browser = globalThis.browser;
|
|
45
44
|
const addEventListener = (type, listener, options) => globalThis.addEventListener(type, listener, options);
|
|
46
45
|
const dispatchEvent = event => { try { globalThis.dispatchEvent(event); } catch (error) { /* ignored */ } };
|
|
47
46
|
const CustomEvent = globalThis.CustomEvent;
|
|
@@ -69,17 +68,6 @@ if (document instanceof Document) {
|
|
|
69
68
|
fontFaces.delete(JSON.stringify(key));
|
|
70
69
|
});
|
|
71
70
|
addEventListener(CLEAR_FONTS_EVENT, () => fontFaces = new Map());
|
|
72
|
-
let scriptElement = document.createElement("script");
|
|
73
|
-
scriptElement.src = "data:," + "(" + injectedScript.toString() + ")()";
|
|
74
|
-
(document.documentElement || document).appendChild(scriptElement);
|
|
75
|
-
scriptElement.remove();
|
|
76
|
-
if (browser && browser.runtime && browser.runtime.getURL) {
|
|
77
|
-
scriptElement = document.createElement("script");
|
|
78
|
-
scriptElement.src = browser.runtime.getURL("/lib/single-file-hooks-frames.js");
|
|
79
|
-
scriptElement.async = false;
|
|
80
|
-
(document.documentElement || document).appendChild(scriptElement);
|
|
81
|
-
scriptElement.remove();
|
|
82
|
-
}
|
|
83
71
|
}
|
|
84
72
|
|
|
85
73
|
export {
|
|
@@ -135,80 +123,4 @@ function loadDeferredImagesResetZoomLevel(options) {
|
|
|
135
123
|
} else {
|
|
136
124
|
dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_RESET_EVENT));
|
|
137
125
|
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function injectedScript() {
|
|
141
|
-
if (typeof globalThis == "undefined") {
|
|
142
|
-
window.globalThis = window;
|
|
143
|
-
}
|
|
144
|
-
const document = globalThis.document;
|
|
145
|
-
const console = globalThis.console;
|
|
146
|
-
const dispatchEvent = event => globalThis.dispatchEvent(event);
|
|
147
|
-
const CustomEvent = globalThis.CustomEvent;
|
|
148
|
-
const FileReader = globalThis.FileReader;
|
|
149
|
-
const Blob = globalThis.Blob;
|
|
150
|
-
const warn = (console && console.warn && ((...args) => console.warn(...args))) || (() => { });
|
|
151
|
-
const NEW_FONT_FACE_EVENT = "single-file-new-font-face";
|
|
152
|
-
const DELETE_FONT_EVENT = "single-file-delete-font";
|
|
153
|
-
const CLEAR_FONTS_EVENT = "single-file-clear-fonts";
|
|
154
|
-
const FONT_STYLE_PROPERTIES = {
|
|
155
|
-
family: "font-family",
|
|
156
|
-
style: "font-style",
|
|
157
|
-
weight: "font-weight",
|
|
158
|
-
stretch: "font-stretch",
|
|
159
|
-
unicodeRange: "unicode-range",
|
|
160
|
-
variant: "font-variant",
|
|
161
|
-
featureSettings: "font-feature-settings"
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
if (globalThis.FontFace) {
|
|
165
|
-
const FontFace = globalThis.FontFace;
|
|
166
|
-
let warningFontFaceDisplayed;
|
|
167
|
-
globalThis.FontFace = function () {
|
|
168
|
-
if (!warningFontFaceDisplayed) {
|
|
169
|
-
warn("SingleFile is hooking the FontFace constructor, document.fonts.delete and document.fonts.clear to handle dynamically loaded fonts.");
|
|
170
|
-
warningFontFaceDisplayed = true;
|
|
171
|
-
}
|
|
172
|
-
getDetailObject(...arguments).then(detail => dispatchEvent(new CustomEvent(NEW_FONT_FACE_EVENT, { detail })));
|
|
173
|
-
return new FontFace(...arguments);
|
|
174
|
-
};
|
|
175
|
-
globalThis.FontFace.toString = function () { return "function FontFace() { [native code] }"; };
|
|
176
|
-
const deleteFont = document.fonts.delete;
|
|
177
|
-
document.fonts.delete = function (fontFace) {
|
|
178
|
-
getDetailObject(fontFace.family).then(detail => dispatchEvent(new CustomEvent(DELETE_FONT_EVENT, { detail })));
|
|
179
|
-
return deleteFont.call(document.fonts, fontFace);
|
|
180
|
-
};
|
|
181
|
-
document.fonts.delete.toString = function () { return "function delete() { [native code] }"; };
|
|
182
|
-
const clearFonts = document.fonts.clear;
|
|
183
|
-
document.fonts.clear = function () {
|
|
184
|
-
dispatchEvent(new CustomEvent(CLEAR_FONTS_EVENT));
|
|
185
|
-
return clearFonts.call(document.fonts);
|
|
186
|
-
};
|
|
187
|
-
document.fonts.clear.toString = function () { return "function clear() { [native code] }"; };
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async function getDetailObject(fontFamily, src, descriptors) {
|
|
191
|
-
const detail = {};
|
|
192
|
-
detail["font-family"] = fontFamily;
|
|
193
|
-
detail.src = src;
|
|
194
|
-
if (descriptors) {
|
|
195
|
-
Object.keys(descriptors).forEach(descriptor => {
|
|
196
|
-
if (FONT_STYLE_PROPERTIES[descriptor]) {
|
|
197
|
-
detail[FONT_STYLE_PROPERTIES[descriptor]] = descriptors[descriptor];
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
return new Promise(resolve => {
|
|
202
|
-
if (detail.src instanceof ArrayBuffer) {
|
|
203
|
-
const reader = new FileReader();
|
|
204
|
-
reader.readAsDataURL(new Blob([detail.src]));
|
|
205
|
-
reader.addEventListener("load", () => {
|
|
206
|
-
detail.src = "url(" + reader.result + ")";
|
|
207
|
-
resolve(detail);
|
|
208
|
-
});
|
|
209
|
-
} else {
|
|
210
|
-
resolve(detail);
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
126
|
}
|
package/processors/index.js
CHANGED
|
@@ -22,13 +22,11 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import * as frameTree from "./frame-tree/content/content-frame-tree.js";
|
|
25
|
-
import * as hooks from "./hooks/content/content-hooks.js";
|
|
26
25
|
import * as hooksFrames from "./hooks/content/content-hooks-frames.js";
|
|
27
26
|
import * as lazy from "./lazy/content/content-lazy-loader.js";
|
|
28
27
|
|
|
29
28
|
export {
|
|
30
29
|
frameTree,
|
|
31
|
-
hooks,
|
|
32
30
|
hooksFrames,
|
|
33
31
|
lazy
|
|
34
32
|
};
|
package/single-file-core.js
CHANGED
|
@@ -116,8 +116,7 @@ const STAGES = [{
|
|
|
116
116
|
parallel: [
|
|
117
117
|
{ option: "blockVideos", action: "insertMissingVideoPosters" },
|
|
118
118
|
{ action: "resolveStylesheetURLs" },
|
|
119
|
-
{ option: "!removeFrames", action: "resolveFrameURLs" }
|
|
120
|
-
{ action: "resolveHtmlImportURLs" }
|
|
119
|
+
{ option: "!removeFrames", action: "resolveFrameURLs" }
|
|
121
120
|
]
|
|
122
121
|
}, {
|
|
123
122
|
sequential: [
|
|
@@ -137,8 +136,7 @@ const STAGES = [{
|
|
|
137
136
|
],
|
|
138
137
|
parallel: [
|
|
139
138
|
{ option: "removeAlternativeFonts", action: "removeAlternativeFonts" },
|
|
140
|
-
{ option: "!removeFrames", action: "processFrames" }
|
|
141
|
-
{ option: "!removeImports", action: "processHtmlImports" },
|
|
139
|
+
{ option: "!removeFrames", action: "processFrames" }
|
|
142
140
|
]
|
|
143
141
|
}, {
|
|
144
142
|
sequential: [
|
|
@@ -178,7 +176,6 @@ class Runner {
|
|
|
178
176
|
this.options.videos = docData.videos;
|
|
179
177
|
this.options.usedFonts = docData.usedFonts;
|
|
180
178
|
this.options.shadowRoots = docData.shadowRoots;
|
|
181
|
-
this.options.imports = docData.imports;
|
|
182
179
|
this.options.referrer = docData.referrer;
|
|
183
180
|
this.markedElements = docData.markedElements;
|
|
184
181
|
this.invalidElements = docData.invalidElements;
|
|
@@ -212,9 +209,6 @@ class Runner {
|
|
|
212
209
|
if (this.options.frames) {
|
|
213
210
|
this.options.frames.forEach(cancelRunner);
|
|
214
211
|
}
|
|
215
|
-
if (this.options.imports) {
|
|
216
|
-
this.options.imports.forEach(cancelRunner);
|
|
217
|
-
}
|
|
218
212
|
}
|
|
219
213
|
|
|
220
214
|
function cancelRunner(resourceData) {
|
|
@@ -428,9 +422,6 @@ class Processor {
|
|
|
428
422
|
if (!this.options.saveRawPage && !this.options.removeFrames && this.options.frames) {
|
|
429
423
|
this.options.frames.forEach(frameData => this.maxResources += frameData.maxResources || 0);
|
|
430
424
|
}
|
|
431
|
-
if (!this.options.removeImports && this.options.imports) {
|
|
432
|
-
this.options.imports.forEach(importData => this.maxResources += importData.maxResources || 0);
|
|
433
|
-
}
|
|
434
425
|
this.stats.set("processed", "resources", this.maxResources);
|
|
435
426
|
}
|
|
436
427
|
|
|
@@ -794,6 +785,7 @@ class Processor {
|
|
|
794
785
|
this.doc.querySelectorAll("link[rel*=\"icon\"]").forEach(element => element.remove());
|
|
795
786
|
}
|
|
796
787
|
this.doc.querySelectorAll("a[ping]").forEach(element => element.removeAttribute("ping"));
|
|
788
|
+
this.doc.querySelectorAll("link[rel=import][href]").forEach(element => element.remove());
|
|
797
789
|
}
|
|
798
790
|
|
|
799
791
|
replaceInvalidElements() {
|
|
@@ -1134,7 +1126,6 @@ class Processor {
|
|
|
1134
1126
|
options.videos = frameData.videos;
|
|
1135
1127
|
options.usedFonts = frameData.usedFonts;
|
|
1136
1128
|
options.shadowRoots = frameData.shadowRoots;
|
|
1137
|
-
options.imports = frameData.imports;
|
|
1138
1129
|
frameData.runner = new Runner(options);
|
|
1139
1130
|
frameData.frameElement = frameElement;
|
|
1140
1131
|
await frameData.runner.loadPage();
|
|
@@ -1197,49 +1188,6 @@ class Processor {
|
|
|
1197
1188
|
}
|
|
1198
1189
|
}
|
|
1199
1190
|
|
|
1200
|
-
async resolveHtmlImportURLs() {
|
|
1201
|
-
const linkElements = Array.from(this.doc.querySelectorAll("link[rel=import][href]"));
|
|
1202
|
-
await Promise.all(linkElements.map(async linkElement => {
|
|
1203
|
-
const resourceURL = linkElement.href;
|
|
1204
|
-
if (this.options.saveOriginalURLs && !isDataURL(resourceURL)) {
|
|
1205
|
-
linkElement.setAttribute("data-sf-original-href", resourceURL);
|
|
1206
|
-
}
|
|
1207
|
-
linkElement.removeAttribute("href");
|
|
1208
|
-
const options = Object.create(this.options);
|
|
1209
|
-
options.insertSingleFileComment = false;
|
|
1210
|
-
options.insertCanonicalLink = false;
|
|
1211
|
-
options.insertMetaNoIndex = false;
|
|
1212
|
-
options.saveFavicon = false;
|
|
1213
|
-
options.removeUnusedStyles = false;
|
|
1214
|
-
options.removeAlternativeMedias = false;
|
|
1215
|
-
options.removeUnusedFonts = false;
|
|
1216
|
-
options.removeHiddenElements = false;
|
|
1217
|
-
options.url = resourceURL;
|
|
1218
|
-
const attributeValue = linkElement.getAttribute(util.HTML_IMPORT_ATTRIBUTE_NAME);
|
|
1219
|
-
if (attributeValue) {
|
|
1220
|
-
const importData = options.imports[Number(attributeValue)];
|
|
1221
|
-
if (importData) {
|
|
1222
|
-
options.content = importData.content;
|
|
1223
|
-
importData.runner = new Runner(options);
|
|
1224
|
-
await importData.runner.loadPage();
|
|
1225
|
-
await importData.runner.initialize();
|
|
1226
|
-
if (!options.removeImports) {
|
|
1227
|
-
importData.maxResources = importData.runner.batchRequest.getMaxResources();
|
|
1228
|
-
}
|
|
1229
|
-
importData.runner.getStyleSheets().forEach(stylesheet => {
|
|
1230
|
-
const importedStyleElement = this.doc.createElement("style");
|
|
1231
|
-
linkElement.insertAdjacentElement("afterEnd", importedStyleElement);
|
|
1232
|
-
this.stylesheets.set(importedStyleElement, stylesheet);
|
|
1233
|
-
});
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
if (options.removeImports) {
|
|
1237
|
-
linkElement.remove();
|
|
1238
|
-
this.stats.add("discarded", "HTML imports", 1);
|
|
1239
|
-
}
|
|
1240
|
-
}));
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
1191
|
removeUnusedStyles() {
|
|
1244
1192
|
if (!this.mediaAllInfo) {
|
|
1245
1193
|
this.mediaAllInfo = util.getMediaAllInfo(this.doc, this.stylesheets, this.styles);
|
|
@@ -1393,26 +1341,6 @@ class Processor {
|
|
|
1393
1341
|
}
|
|
1394
1342
|
}
|
|
1395
1343
|
|
|
1396
|
-
async processHtmlImports() {
|
|
1397
|
-
const linkElements = Array.from(this.doc.querySelectorAll("link[rel=import]"));
|
|
1398
|
-
await Promise.all(linkElements.map(async linkElement => {
|
|
1399
|
-
const attributeValue = linkElement.getAttribute(util.HTML_IMPORT_ATTRIBUTE_NAME);
|
|
1400
|
-
if (attributeValue) {
|
|
1401
|
-
const importData = this.options.imports[Number(attributeValue)];
|
|
1402
|
-
if (importData.runner) {
|
|
1403
|
-
this.stats.add("processed", "HTML imports", 1);
|
|
1404
|
-
await importData.runner.run();
|
|
1405
|
-
const pageData = await importData.runner.getPageData();
|
|
1406
|
-
linkElement.removeAttribute(util.HTML_IMPORT_ATTRIBUTE_NAME);
|
|
1407
|
-
linkElement.setAttribute("href", "data:text/html," + pageData.content);
|
|
1408
|
-
this.stats.addAll(pageData);
|
|
1409
|
-
} else {
|
|
1410
|
-
this.stats.add("discarded", "HTML imports", 1);
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
}));
|
|
1414
|
-
}
|
|
1415
|
-
|
|
1416
1344
|
replaceStylesheets() {
|
|
1417
1345
|
this.doc.querySelectorAll("style").forEach(styleElement => {
|
|
1418
1346
|
const stylesheetInfo = this.stylesheets.get(styleElement);
|
|
@@ -2280,7 +2208,6 @@ const STATS_DEFAULT_VALUES = {
|
|
|
2280
2208
|
discarded: {
|
|
2281
2209
|
"HTML bytes": 0,
|
|
2282
2210
|
"hidden elements": 0,
|
|
2283
|
-
"HTML imports": 0,
|
|
2284
2211
|
scripts: 0,
|
|
2285
2212
|
objects: 0,
|
|
2286
2213
|
"audio sources": 0,
|
|
@@ -2295,7 +2222,6 @@ const STATS_DEFAULT_VALUES = {
|
|
|
2295
2222
|
processed: {
|
|
2296
2223
|
"HTML bytes": 0,
|
|
2297
2224
|
"hidden elements": 0,
|
|
2298
|
-
"HTML imports": 0,
|
|
2299
2225
|
scripts: 0,
|
|
2300
2226
|
objects: 0,
|
|
2301
2227
|
"audio sources": 0,
|
|
@@ -21,19 +21,4 @@
|
|
|
21
21
|
* Source.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const browser = globalThis.browser;
|
|
27
|
-
const document = globalThis.document;
|
|
28
|
-
const Document = globalThis.Document;
|
|
29
|
-
|
|
30
|
-
if (document instanceof Document) {
|
|
31
|
-
const scriptElement = document.createElement("script");
|
|
32
|
-
scriptElement.async = false;
|
|
33
|
-
if (browser && browser.runtime && browser.runtime.getURL) {
|
|
34
|
-
scriptElement.src = browser.runtime.getURL("/lib/single-file-hooks.js");
|
|
35
|
-
scriptElement.async = false;
|
|
36
|
-
}
|
|
37
|
-
(document.documentElement || document).appendChild(scriptElement);
|
|
38
|
-
scriptElement.remove();
|
|
39
|
-
}
|
|
24
|
+
export * from "./processors/frame-tree/content/content-frame-tree.js";
|
package/single-file-helper.js
CHANGED
|
@@ -39,7 +39,6 @@ const IMAGE_ATTRIBUTE_NAME = "data-single-file-image";
|
|
|
39
39
|
const POSTER_ATTRIBUTE_NAME = "data-single-file-poster";
|
|
40
40
|
const VIDEO_ATTRIBUTE_NAME = "data-single-file-video";
|
|
41
41
|
const CANVAS_ATTRIBUTE_NAME = "data-single-file-canvas";
|
|
42
|
-
const HTML_IMPORT_ATTRIBUTE_NAME = "data-single-file-import";
|
|
43
42
|
const STYLE_ATTRIBUTE_NAME = "data-single-file-movable-style";
|
|
44
43
|
const INPUT_VALUE_ATTRIBUTE_NAME = "data-single-file-input-value";
|
|
45
44
|
const LAZY_SRC_ATTRIBUTE_NAME = "data-single-file-lazy-loaded-src";
|
|
@@ -90,7 +89,6 @@ export {
|
|
|
90
89
|
CANVAS_ATTRIBUTE_NAME,
|
|
91
90
|
INPUT_VALUE_ATTRIBUTE_NAME,
|
|
92
91
|
SHADOW_ROOT_ATTRIBUTE_NAME,
|
|
93
|
-
HTML_IMPORT_ATTRIBUTE_NAME,
|
|
94
92
|
STYLE_ATTRIBUTE_NAME,
|
|
95
93
|
LAZY_SRC_ATTRIBUTE_NAME,
|
|
96
94
|
STYLESHEET_ATTRIBUTE_NAME,
|
|
@@ -165,7 +163,6 @@ function preProcessDoc(doc, win, options) {
|
|
|
165
163
|
videos: [],
|
|
166
164
|
usedFonts: [],
|
|
167
165
|
shadowRoots: [],
|
|
168
|
-
imports: [],
|
|
169
166
|
markedElements: []
|
|
170
167
|
};
|
|
171
168
|
}
|
|
@@ -178,14 +175,13 @@ function preProcessDoc(doc, win, options) {
|
|
|
178
175
|
videos: elementsInfo.videos,
|
|
179
176
|
usedFonts: Array.from(elementsInfo.usedFonts.values()),
|
|
180
177
|
shadowRoots: elementsInfo.shadowRoots,
|
|
181
|
-
imports: elementsInfo.imports,
|
|
182
178
|
referrer: doc.referrer,
|
|
183
179
|
markedElements: elementsInfo.markedElements,
|
|
184
180
|
invalidElements
|
|
185
181
|
};
|
|
186
182
|
}
|
|
187
183
|
|
|
188
|
-
function getElementsInfo(win, doc, element, options, data = { usedFonts: new Map(), canvases: [], images: [], posters: [], videos: [], shadowRoots: [],
|
|
184
|
+
function getElementsInfo(win, doc, element, options, data = { usedFonts: new Map(), canvases: [], images: [], posters: [], videos: [], shadowRoots: [], markedElements: [] }, ascendantHidden) {
|
|
189
185
|
const elements = Array.from(element.childNodes).filter(node => (node instanceof win.HTMLElement) || (node instanceof win.SVGElement));
|
|
190
186
|
elements.forEach(element => {
|
|
191
187
|
let elementHidden, elementKept, computedStyle;
|
|
@@ -329,13 +325,6 @@ function getResourcesInfo(win, doc, element, options, data, elementHidden, compu
|
|
|
329
325
|
data.markedElements.push(element);
|
|
330
326
|
}
|
|
331
327
|
}
|
|
332
|
-
if (element.tagName == "LINK") {
|
|
333
|
-
if (element.import && element.import.documentElement) {
|
|
334
|
-
data.imports.push({ content: serialize(element.import) });
|
|
335
|
-
element.setAttribute(HTML_IMPORT_ATTRIBUTE_NAME, data.imports.length - 1);
|
|
336
|
-
data.markedElements.push(element);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
328
|
if (element.tagName == "INPUT") {
|
|
340
329
|
if (element.type != "password") {
|
|
341
330
|
element.setAttribute(INPUT_VALUE_ATTRIBUTE_NAME, element.value);
|
|
@@ -429,7 +418,7 @@ function postProcessDoc(doc, markedElements, invalidElements) {
|
|
|
429
418
|
doc.head.querySelectorAll("*:not(base):not(link):not(meta):not(noscript):not(script):not(style):not(template):not(title)").forEach(element => element.removeAttribute("hidden"));
|
|
430
419
|
}
|
|
431
420
|
if (!markedElements) {
|
|
432
|
-
const singleFileAttributes = [REMOVED_CONTENT_ATTRIBUTE_NAME, HIDDEN_FRAME_ATTRIBUTE_NAME, HIDDEN_CONTENT_ATTRIBUTE_NAME, PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME, IMAGE_ATTRIBUTE_NAME, POSTER_ATTRIBUTE_NAME, VIDEO_ATTRIBUTE_NAME, CANVAS_ATTRIBUTE_NAME, INPUT_VALUE_ATTRIBUTE_NAME, SHADOW_ROOT_ATTRIBUTE_NAME,
|
|
421
|
+
const singleFileAttributes = [REMOVED_CONTENT_ATTRIBUTE_NAME, HIDDEN_FRAME_ATTRIBUTE_NAME, HIDDEN_CONTENT_ATTRIBUTE_NAME, PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME, IMAGE_ATTRIBUTE_NAME, POSTER_ATTRIBUTE_NAME, VIDEO_ATTRIBUTE_NAME, CANVAS_ATTRIBUTE_NAME, INPUT_VALUE_ATTRIBUTE_NAME, SHADOW_ROOT_ATTRIBUTE_NAME, STYLESHEET_ATTRIBUTE_NAME, ASYNC_SCRIPT_ATTRIBUTE_NAME];
|
|
433
422
|
markedElements = doc.querySelectorAll(singleFileAttributes.map(name => "[" + name + "]").join(","));
|
|
434
423
|
}
|
|
435
424
|
markedElements.forEach(element => {
|
|
@@ -444,7 +433,6 @@ function postProcessDoc(doc, markedElements, invalidElements) {
|
|
|
444
433
|
element.removeAttribute(CANVAS_ATTRIBUTE_NAME);
|
|
445
434
|
element.removeAttribute(INPUT_VALUE_ATTRIBUTE_NAME);
|
|
446
435
|
element.removeAttribute(SHADOW_ROOT_ATTRIBUTE_NAME);
|
|
447
|
-
element.removeAttribute(HTML_IMPORT_ATTRIBUTE_NAME);
|
|
448
436
|
element.removeAttribute(STYLESHEET_ATTRIBUTE_NAME);
|
|
449
437
|
element.removeAttribute(ASYNC_SCRIPT_ATTRIBUTE_NAME);
|
|
450
438
|
element.removeAttribute(STYLE_ATTRIBUTE_NAME);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2010-2022 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
|
+
import "./processors/hooks/content/content-hooks-frames-web.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2010-2022 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
|
+
import "./processors/hooks/content/content-hooks-web.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2010-2022 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
|
+
import "./common/content-infobar-web.js";
|
package/single-file-util.js
CHANGED
|
@@ -179,7 +179,6 @@ function getInstance(utilOptions) {
|
|
|
179
179
|
POSTER_ATTRIBUTE_NAME: helper.POSTER_ATTRIBUTE_NAME,
|
|
180
180
|
VIDEO_ATTRIBUTE_NAME: helper.VIDEO_ATTRIBUTE_NAME,
|
|
181
181
|
CANVAS_ATTRIBUTE_NAME: helper.CANVAS_ATTRIBUTE_NAME,
|
|
182
|
-
HTML_IMPORT_ATTRIBUTE_NAME: helper.HTML_IMPORT_ATTRIBUTE_NAME,
|
|
183
182
|
STYLE_ATTRIBUTE_NAME: helper.STYLE_ATTRIBUTE_NAME,
|
|
184
183
|
INPUT_VALUE_ATTRIBUTE_NAME: helper.INPUT_VALUE_ATTRIBUTE_NAME,
|
|
185
184
|
SHADOW_ROOT_ATTRIBUTE_NAME: helper.SHADOW_ROOT_ATTRIBUTE_NAME,
|
|
File without changes
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
* SOFTWARE.
|
|
49
49
|
*/
|
|
50
50
|
|
|
51
|
+
import * as cssTree from "./css-tree.js";
|
|
51
52
|
const REGEXP_SIMPLE_QUOTES_STRING = /^'(.*?)'$/;
|
|
52
53
|
const REGEXP_DOUBLE_QUOTES_STRING = /^"(.*?)"$/;
|
|
53
54
|
|
|
@@ -100,28 +101,6 @@ const fontStretchKeywords = [
|
|
|
100
101
|
"ultra-expanded"
|
|
101
102
|
];
|
|
102
103
|
|
|
103
|
-
const cssFontSizeKeywords = [
|
|
104
|
-
"xx-small",
|
|
105
|
-
"x-small",
|
|
106
|
-
"small",
|
|
107
|
-
"medium",
|
|
108
|
-
"large",
|
|
109
|
-
"x-large",
|
|
110
|
-
"xx-large",
|
|
111
|
-
"larger",
|
|
112
|
-
"smaller"
|
|
113
|
-
];
|
|
114
|
-
|
|
115
|
-
const cssListHelpers = {
|
|
116
|
-
splitBySpaces,
|
|
117
|
-
split,
|
|
118
|
-
splitByCommas
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
const helpers = {
|
|
122
|
-
isSize
|
|
123
|
-
};
|
|
124
|
-
|
|
125
104
|
const errorPrefix = "[parse-css-font] ";
|
|
126
105
|
|
|
127
106
|
export {
|
|
@@ -129,15 +108,11 @@ export {
|
|
|
129
108
|
};
|
|
130
109
|
|
|
131
110
|
function parse(value) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (value === "") {
|
|
136
|
-
throw error("Cannot parse an empty string.");
|
|
137
|
-
}
|
|
138
|
-
if (systemFontKeywords.indexOf(value) !== -1) {
|
|
139
|
-
return { system: value };
|
|
111
|
+
const stringValue = cssTree.generate(value);
|
|
112
|
+
if (systemFontKeywords.indexOf(stringValue) !== -1) {
|
|
113
|
+
return { system: stringValue };
|
|
140
114
|
}
|
|
115
|
+
const tokens = value.children;
|
|
141
116
|
|
|
142
117
|
const font = {
|
|
143
118
|
lineHeight: "normal",
|
|
@@ -148,10 +123,8 @@ function parse(value) {
|
|
|
148
123
|
};
|
|
149
124
|
|
|
150
125
|
let isLocked = false;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
for (; token; token = tokens.shift()) {
|
|
154
|
-
|
|
126
|
+
for (let tokenNode = tokens.head; tokenNode; tokenNode = tokenNode.next) {
|
|
127
|
+
const token = cssTree.generate(tokenNode.data);
|
|
155
128
|
if (token === "normal" || globalKeywords.indexOf(token) !== -1) {
|
|
156
129
|
["style", "variant", "weight", "stretch"].forEach((prop) => {
|
|
157
130
|
font[prop] = token;
|
|
@@ -184,19 +157,29 @@ function parse(value) {
|
|
|
184
157
|
continue;
|
|
185
158
|
}
|
|
186
159
|
|
|
187
|
-
if (
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
160
|
+
if (tokenNode.data.type == "Dimension") {
|
|
161
|
+
font.size = cssTree.generate(tokenNode.data);
|
|
162
|
+
tokenNode = tokenNode.next;
|
|
163
|
+
if (tokenNode && tokenNode.data.type == "Operator" && tokenNode.data.value == "/" && tokenNode.next) {
|
|
164
|
+
tokenNode = tokenNode.next;
|
|
165
|
+
font.lineHeight = cssTree.generate(tokenNode.data);
|
|
166
|
+
tokenNode = tokenNode.next;
|
|
167
|
+
} else if (tokens.head.data.type == "Operator" && tokens.head.data.value == "/" && tokens.head.next) {
|
|
168
|
+
font.lineHeight = cssTree.generate(tokens.head.next.data);
|
|
169
|
+
tokenNode = tokens.head.next.next;
|
|
195
170
|
}
|
|
196
|
-
if (!
|
|
171
|
+
if (!tokenNode) {
|
|
197
172
|
throw error("Missing required font-family.");
|
|
198
173
|
}
|
|
199
|
-
font.family =
|
|
174
|
+
font.family = [];
|
|
175
|
+
for (; tokenNode; tokenNode = tokenNode.next) {
|
|
176
|
+
while (tokenNode && tokenNode.data.type == "Operator" && tokenNode.data.value == ",") {
|
|
177
|
+
tokenNode = tokenNode.next;
|
|
178
|
+
}
|
|
179
|
+
if (tokenNode) {
|
|
180
|
+
font.family.push(removeQuotes(cssTree.generate(tokenNode.data)));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
200
183
|
return font;
|
|
201
184
|
}
|
|
202
185
|
|
|
@@ -217,122 +200,6 @@ function error(message) {
|
|
|
217
200
|
return new Error(errorPrefix + message);
|
|
218
201
|
}
|
|
219
202
|
|
|
220
|
-
function parseLineHeight(value) {
|
|
221
|
-
const parsed = parseFloat(value);
|
|
222
|
-
if (parsed.toString() === value) {
|
|
223
|
-
return parsed;
|
|
224
|
-
}
|
|
225
|
-
return value;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Splits a CSS declaration value (shorthand) using provided separators
|
|
230
|
-
* as the delimiters.
|
|
231
|
-
*/
|
|
232
|
-
function split(
|
|
233
|
-
/**
|
|
234
|
-
* A CSS declaration value (shorthand).
|
|
235
|
-
*/
|
|
236
|
-
value,
|
|
237
|
-
/**
|
|
238
|
-
* Any number of separator characters used for splitting.
|
|
239
|
-
*/
|
|
240
|
-
separators,
|
|
241
|
-
{
|
|
242
|
-
last = false,
|
|
243
|
-
} = {},
|
|
244
|
-
) {
|
|
245
|
-
if (typeof value !== "string") {
|
|
246
|
-
throw new TypeError("expected a string");
|
|
247
|
-
}
|
|
248
|
-
if (!Array.isArray(separators)) {
|
|
249
|
-
throw new TypeError("expected a string array of separators");
|
|
250
|
-
}
|
|
251
|
-
if (typeof last !== "boolean") {
|
|
252
|
-
throw new TypeError("expected a Boolean value for options.last");
|
|
253
|
-
}
|
|
254
|
-
const array = [];
|
|
255
|
-
let current = "";
|
|
256
|
-
let splitMe = false;
|
|
257
|
-
|
|
258
|
-
let func = 0;
|
|
259
|
-
let quote = false;
|
|
260
|
-
let escape = false;
|
|
261
|
-
|
|
262
|
-
for (const char of value) {
|
|
263
|
-
|
|
264
|
-
if (quote) {
|
|
265
|
-
if (escape) {
|
|
266
|
-
escape = false;
|
|
267
|
-
} else if (char === "\\") {
|
|
268
|
-
escape = true;
|
|
269
|
-
} else if (char === quote) {
|
|
270
|
-
quote = false;
|
|
271
|
-
}
|
|
272
|
-
} else if (char === "\"" || char === "'") {
|
|
273
|
-
quote = char;
|
|
274
|
-
} else if (char === "(") {
|
|
275
|
-
func += 1;
|
|
276
|
-
} else if (char === ")") {
|
|
277
|
-
if (func > 0) {
|
|
278
|
-
func -= 1;
|
|
279
|
-
}
|
|
280
|
-
} else if (func === 0) {
|
|
281
|
-
if (separators.indexOf(char) !== -1) {
|
|
282
|
-
splitMe = true;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (splitMe) {
|
|
287
|
-
if (current !== "") {
|
|
288
|
-
array.push(current.trim());
|
|
289
|
-
}
|
|
290
|
-
current = "";
|
|
291
|
-
splitMe = false;
|
|
292
|
-
} else {
|
|
293
|
-
current += char;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
if (last || current !== "") {
|
|
298
|
-
array.push(current.trim());
|
|
299
|
-
}
|
|
300
|
-
return array;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Splits a CSS declaration value (shorthand) using whitespace characters
|
|
305
|
-
* as the delimiters.
|
|
306
|
-
*/
|
|
307
|
-
function splitBySpaces(
|
|
308
|
-
/**
|
|
309
|
-
* A CSS declaration value (shorthand).
|
|
310
|
-
*/
|
|
311
|
-
value,
|
|
312
|
-
) {
|
|
313
|
-
const spaces = [" ", "\n", "\t"];
|
|
314
|
-
return split(value, spaces);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Splits a CSS declaration value (shorthand) using commas as the delimiters.
|
|
319
|
-
*/
|
|
320
|
-
function splitByCommas(
|
|
321
|
-
/**
|
|
322
|
-
* A CSS declaration value (shorthand).
|
|
323
|
-
*/
|
|
324
|
-
value,
|
|
325
|
-
) {
|
|
326
|
-
const comma = ",";
|
|
327
|
-
return split(value, [comma], { last: true });
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
function isSize(value) {
|
|
331
|
-
return !isNaN(parseFloat(value))
|
|
332
|
-
|| value.indexOf("/") !== -1
|
|
333
|
-
|| cssFontSizeKeywords.indexOf(value) !== -1;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
203
|
function removeQuotes(string) {
|
|
337
204
|
if (string.match(REGEXP_SIMPLE_QUOTES_STRING)) {
|
|
338
205
|
string = string.replace(REGEXP_SIMPLE_QUOTES_STRING, "$1");
|