Sfoglia il codice sorgente

added 'maximum size of embedded resource" option

Gildas 7 anni fa
parent
commit
c9aa95e547

+ 7 - 2
extension/core/bg/config.js

@@ -55,7 +55,11 @@ singlefile.config = (() => {
 		if (config.shadowEnabled == undefined) {
 			config.shadowEnabled = true;
 			localStorage.config = JSON.stringify(config);
-		}		
+		}
+		if (config.maxResourceSize == undefined) {
+			config.maxResourceSize = 10;
+			localStorage.config = JSON.stringify(config);
+		}
 	}
 
 	return {
@@ -75,7 +79,8 @@ singlefile.config = (() => {
 				lazyLoadImages: true,
 				appendSaveDate: true,
 				contextMenuEnabled: true,
-				shadowEnabled: true
+				shadowEnabled: true,
+				maxResourceSize: 10
 			};
 		},
 		reset() {

+ 4 - 1
extension/ui/bg/options.js

@@ -37,6 +37,7 @@
 		const contextMenuEnabledInput = document.getElementById("contextMenuEnabledInput");
 		const appendSaveDateInput = document.getElementById("appendSaveDateInput");
 		const shadowEnabledInput = document.getElementById("shadowEnabledInput");
+		const maxResourceSizeInput = document.getElementById("maxResourceSizeInput");
 		document.getElementById("resetButton").addEventListener("click", () => {
 			bgPage.singlefile.config.reset();
 			refresh();
@@ -59,6 +60,7 @@
 			contextMenuEnabledInput.checked = config.contextMenuEnabled;
 			appendSaveDateInput.checked = config.appendSaveDate;
 			shadowEnabledInput.checked = config.shadowEnabled;
+			maxResourceSizeInput.value = config.maxResourceSize;
 		}
 
 		function update() {
@@ -74,7 +76,8 @@
 				lazyLoadImages: lazyLoadImagesInput.checked,
 				contextMenuEnabled: contextMenuEnabledInput.checked,
 				appendSaveDate: appendSaveDateInput.checked,
-				shadowEnabled: shadowEnabledInput.checked
+				shadowEnabled: shadowEnabledInput.checked,
+				maxResourceSize: maxResourceSizeInput.value
 			});
 			bgPage.singlefile.ui.update();
 		}

+ 6 - 0
extension/ui/pages/help.html

@@ -66,6 +66,12 @@
 							<u>check</u> this option</p>
 					</li>
 
+					<li>
+						<span class="option">maximum size of embedded resource (Mb)</span>
+						<p>Specify the maximum size of embedded resources in megabytes.
+						</p>
+					</li>
+
 					<li>
 						<span class="option">load lazy loaded images</span>
 						<p>Check this option to try loading all the images that are not already loaded. This helps to get all the images without

+ 9 - 0
extension/ui/pages/options.css

@@ -31,6 +31,7 @@ button:active {
 #popupContent .option {
     display: flex;
     justify-content: space-between;
+    margin-top: 5px;
 }
 
 #popupContent .bottom {
@@ -43,6 +44,14 @@ input[type="checkbox"] {
     top: -2px;
 }
 
+input[type="number"] {
+    margin-left: 30px;
+    max-width: 40px;
+    text-align: right;
+    position: relative;
+    top: -2px;
+}
+
 button {
     padding-left: 10px;
     padding-right: 10px;

+ 4 - 0
extension/ui/pages/options.html

@@ -26,6 +26,10 @@
 				<input type="checkbox" id="appendSaveDateInput">
 			</div>
 			<h4>Page content</h4>
+			<div class="option">
+				<label for="maxResourceSizeInput">maximum size of embedded resource (Mb)</label>
+				<input type="number" id="maxResourceSizeInput">
+			</div>
 			<div class="option">
 				<label for="lazyLoadImagesInput">load lazy loaded images</label>
 				<input type="checkbox" id="lazyLoadImagesInput">

+ 19 - 7
lib/single-file/single-file-browser.js

@@ -22,6 +22,7 @@
 
 this.SingleFile = (() => {
 
+	const ONE_MB = 1024 * 1024;
 
 	// --------
 	// Download
@@ -29,7 +30,7 @@ this.SingleFile = (() => {
 	let fetchResource;
 
 	class Download {
-		static async getContent(resourceURL, asDataURI) {
+		static async getContent(resourceURL, options) {
 			const requestOptions = { method: "GET" };
 			let resourceContent;
 			if (!fetchResource) {
@@ -38,28 +39,39 @@ this.SingleFile = (() => {
 			try {
 				resourceContent = await fetchResource(resourceURL, requestOptions);
 			} catch (e) {
-				return asDataURI ? "data:base64," : "";
+				return options && options.asDataURI ? "data:base64," : "";
 			}
 			const contentType = resourceContent.headers.get("content-type");
-			if (asDataURI) {
+			if (options && options.asDataURI) {
 				try {
 					const buffer = await resourceContent.arrayBuffer();
-					return "data:" + (contentType || "") + ";" + "base64," + base64.fromByteArray(new Uint8Array(buffer));
+					const dataURI = "data:" + (contentType || "") + ";" + "base64," + base64.fromByteArray(new Uint8Array(buffer));
+					if (options.maxSize && buffer.size > options.maxSize * ONE_MB) {
+						return "data:base64,";
+					} else {
+						return dataURI;
+					}
 				} catch (e) {
 					return "data:base64,";
 				}
 			} else {
 				const matchCharset = contentType && contentType.match(/\s*;\s*charset\s*=\s*"?([^";]*)"?(;|$)/i);
+				let textContent;
 				if (matchCharset && matchCharset[1]) {
 					const charSet = matchCharset[1].toLowerCase();
 					if (charSet != "utf-8") {
 						const arrayBuffer = await resourceContent.arrayBuffer();
-						return (new TextDecoder(charSet)).decode(arrayBuffer);
+						textContent = (new TextDecoder(charSet)).decode(arrayBuffer);
 					} else {
-						return resourceContent.text();
+						textContent = resourceContent.text();
 					}
 				} else {
-					return resourceContent.text();
+					textContent = resourceContent.text();
+				}
+				if (options.maxSize && textContent.length > options.maxSize * ONE_MB) {
+					return "";
+				} else {
+					return textContent;
 				}
 			}
 		}

+ 16 - 15
lib/single-file/single-file-core.js

@@ -184,7 +184,7 @@ const SingleFileCore = (() => {
 			return Array.from(this.requests.keys()).length;
 		}
 
-		async run(beforeListener, afterListener) {
+		async run(beforeListener, afterListener, options) {
 			const resourceURLs = Array.from(this.requests.keys());
 			let indexResource = 1, indexAfterResource = 1;
 			return Promise.all(resourceURLs.map(async resourceURL => {
@@ -193,7 +193,7 @@ const SingleFileCore = (() => {
 				beforeListener({ index: indexResource, max: resourceURLs.length, url: resourceURL, error });
 				indexResource = indexResource + 1;
 				try {
-					const dataURI = await Download.getContent(resourceURL, true);
+					const dataURI = await Download.getContent(resourceURL, { asDataURI: true, maxSize: options.maxResourceSize });
 					resourceRequests.map(resourceRequest => resourceRequest.resolve(dataURI));
 				} catch (responseError) {
 					error = responseError;
@@ -221,7 +221,7 @@ const SingleFileCore = (() => {
 
 		async loadPage(pageContent) {
 			if (!pageContent || this.options.saveRawPage) {
-				pageContent = await Download.getContent(this.baseURI);
+				pageContent = await Download.getContent(this.baseURI, { asDataURI: false, maxSize: this.options.maxResourceSize });
 			}
 			this.dom = DOM.create(pageContent, this.baseURI);
 			this.DOMParser = this.dom.DOMParser;
@@ -243,7 +243,7 @@ const SingleFileCore = (() => {
 		}
 
 		async retrieveResources(beforeListener, afterListener) {
-			await batchRequest.run(beforeListener, afterListener);
+			await batchRequest.run(beforeListener, afterListener, this.options);
 		}
 
 		getPageData() {
@@ -460,7 +460,7 @@ const SingleFileCore = (() => {
 
 		async inlineStylesheets(initialization) {
 			await Promise.all(Array.from(this.doc.querySelectorAll("style")).map(async styleElement => {
-				const stylesheetContent = initialization ? await DomProcessorHelper.resolveImportURLs(styleElement.textContent, this.baseURI) : await DomProcessorHelper.processStylesheet(styleElement.textContent, this.baseURI);
+				const stylesheetContent = initialization ? await DomProcessorHelper.resolveImportURLs(styleElement.textContent, this.baseURI, this.options.maxResourceSize) : await DomProcessorHelper.processStylesheet(styleElement.textContent, this.baseURI);
 				styleElement.textContent = this.options.compressCSS ? this.dom.uglifycss(stylesheetContent) : stylesheetContent;
 			}));
 		}
@@ -468,7 +468,7 @@ const SingleFileCore = (() => {
 		async scripts() {
 			await Promise.all(Array.from(this.doc.querySelectorAll("script[src]")).map(async scriptElement => {
 				if (scriptElement.src) {
-					const scriptContent = await Download.getContent(scriptElement.src);
+					const scriptContent = await Download.getContent(scriptElement.src, { asDataURI: false, maxSize: this.options.maxResourceSize });
 					scriptElement.textContent = scriptContent.replace(/<\/script>/gi, "<\\/script>");
 				}
 				scriptElement.removeAttribute("src");
@@ -496,7 +496,8 @@ const SingleFileCore = (() => {
 							compressHTML: this.options.compressHTML,
 							compressCSS: this.options.compressCSS,
 							lazyLoadImages: this.options.lazyLoadImages,
-							framesData: this.options.framesData
+							framesData: this.options.framesData,
+							maxResourceSize: this.options.maxResourceSize
 						};
 						if (frameData.content) {
 							frameData.processor = new PageProcessor(options);
@@ -563,14 +564,14 @@ const SingleFileCore = (() => {
 
 		async attributeStyles(initialization) {
 			await Promise.all(Array.from(this.doc.querySelectorAll("[style]")).map(async element => {
-				const stylesheetContent = initialization ? await DomProcessorHelper.resolveImportURLs(element.getAttribute("style"), this.baseURI) : await DomProcessorHelper.processStylesheet(element.getAttribute("style"), this.baseURI);
+				const stylesheetContent = initialization ? await DomProcessorHelper.resolveImportURLs(element.getAttribute("style"), this.baseURI, this.options.maxResourceSize) : await DomProcessorHelper.processStylesheet(element.getAttribute("style"), this.baseURI);
 				element.setAttribute("style", stylesheetContent);
 			}));
 		}
 
 		async linkStylesheets() {
 			await Promise.all(Array.from(this.doc.querySelectorAll("link[rel*=stylesheet]")).map(async linkElement => {
-				const stylesheetContent = await DomProcessorHelper.resolveLinkStylesheetURLs(linkElement.href, this.baseURI, linkElement.media);
+				const stylesheetContent = await DomProcessorHelper.resolveLinkStylesheetURLs(linkElement.href, this.baseURI, linkElement.media, this.options.maxResourceSize);
 				const styleElement = this.doc.createElement("style");
 				styleElement.textContent = this.options.compressCSS ? this.dom.uglifycss(stylesheetContent) : stylesheetContent;
 				linkElement.parentElement.replaceChild(styleElement, linkElement);
@@ -599,7 +600,7 @@ const SingleFileCore = (() => {
 			}
 		}
 
-		static async resolveImportURLs(stylesheetContent, baseURI) {
+		static async resolveImportURLs(stylesheetContent, baseURI, maxResourceSize) {
 			stylesheetContent = DomUtil.removeCssComments(stylesheetContent);
 			const imports = DomUtil.getImportFunctions(stylesheetContent);
 			await Promise.all(imports.map(async cssImport => {
@@ -607,7 +608,7 @@ const SingleFileCore = (() => {
 				if (match) {
 					const resourceURL = DomUtil.normalizeURL(match.resourceURL);
 					if (resourceURL != baseURI && resourceURL != ABOUT_BLANK_URI) {
-						let importedStylesheetContent = await Download.getContent(new URL(match.resourceURL, baseURI).href);
+						let importedStylesheetContent = await Download.getContent(new URL(match.resourceURL, baseURI).href, { asDataURI: false, maxSize: maxResourceSize });
 						importedStylesheetContent = DomUtil.wrapMediaQuery(importedStylesheetContent, match.media);
 						if (stylesheetContent.indexOf(cssImport) != -1) {
 							stylesheetContent = stylesheetContent.replace(cssImport, importedStylesheetContent);
@@ -617,7 +618,7 @@ const SingleFileCore = (() => {
 			}));
 			stylesheetContent = DomProcessorHelper.resolveStylesheetURLs(stylesheetContent, baseURI);
 			if (imports.length) {
-				return await DomProcessorHelper.resolveImportURLs(stylesheetContent, baseURI);
+				return await DomProcessorHelper.resolveImportURLs(stylesheetContent, baseURI, maxResourceSize);
 			} else {
 				return stylesheetContent;
 			}
@@ -635,11 +636,11 @@ const SingleFileCore = (() => {
 			return stylesheetContent;
 		}
 
-		static async resolveLinkStylesheetURLs(resourceURL, baseURI, media) {
+		static async resolveLinkStylesheetURLs(resourceURL, baseURI, media, maxResourceSize) {
 			resourceURL = DomUtil.normalizeURL(resourceURL);
 			if (resourceURL && resourceURL != baseURI && resourceURL != ABOUT_BLANK_URI) {
-				let stylesheetContent = await Download.getContent(resourceURL);
-				stylesheetContent = await DomProcessorHelper.resolveImportURLs(stylesheetContent, resourceURL);
+				let stylesheetContent = await Download.getContent(resourceURL, { asDataURI: false, maxSize: maxResourceSize });
+				stylesheetContent = await DomProcessorHelper.resolveImportURLs(stylesheetContent, resourceURL, maxResourceSize);
 				stylesheetContent = DomUtil.wrapMediaQuery(stylesheetContent, media);
 				return stylesheetContent;
 			}