Bladeren bron

implemented auto-save on page unload

Gildas 7 jaren geleden
bovenliggende
commit
574c93fa51

+ 3 - 1
README.MD

@@ -17,7 +17,8 @@ https://www.youtube.com/watch?v=RdV8DBW5b0g
 - Right-click on the SingleFile button and select "Options" to open the options page.
 - To save multiple tabs, select them first and click on the SingleFile button .
 - Use the context menu to save a tab, a selected content, a frame content, unpinned tabs, selected tabs, or all tabs. The context menu also allows activating automatic page saving (i.e. "Auto-save") of a tab, unpinned tabs, or all tabs.
-- With auto-save active, pages will be automatically saved every time after being loaded. The period of time to wait before saving the page can be configured in the options page.
+- With auto-save active, pages will be automatically saved every time after being loaded by default. The period of time to wait before saving the page can be configured in the options page.
+- Check the option "auto-save on page unload" in the options pages to auto-save the page before being unloading instead of after being loaded.
 
 ## More info
 See the extension help in the options page for more detailed information about the options, and technical notes.
@@ -26,6 +27,7 @@ See the extension help in the options page for more detailed information about t
 - All browsers:
   - After installing the extension, if you want to save a tab that was already opened then you need to refresh it first.
   - For security reasons, you cannot save pages hosted on https://chrome.google.com or https://addons.mozilla.org.
+  - Frame contents cannot be saved when "auto-save on page unload" option is checked.
 - Chrome/Opera:
   - You must enable the option "Allow access to file URLs" in the extension page to display the infobar when viewing a saved page, or to save a page stored on the filesystem.
   - If saved pages have file names like "4e5a13fd-6638-4a37-a34b-a3c104ac66b1" instead of the title then it means another extension is conflicting with SingleFile. You can uncheck the option "save pages in background" to circumvent this issue.

+ 37 - 17
extension/core/bg/bg.js

@@ -18,15 +18,13 @@
  *   along with SingleFile.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/* global browser, singlefile, FrameTree, Blob */
+/* global browser, SingleFile, singlefile, FrameTree, Blob */
 
 singlefile.core = (() => {
 
 	const TIMEOUT_PROCESS_START_MESSAGE = 10000;
 
 	const contentScriptFiles = [
-		"/lib/browser-polyfill/custom-browser-polyfill.js",
-		"/extension/index.js",
 		"/extension/ui/content/ui.js",
 		"/lib/single-file/base64.js",
 		"/lib/single-file/uglifycss.js",
@@ -50,25 +48,15 @@ singlefile.core = (() => {
 				if (request.content) {
 					request.url = URL.createObjectURL(new Blob([request.content], { type: "text/html" }));
 				}
-				return browser.downloads.download({ url: request.url, saveAs: request.saveAs, filename: request.filename.replace(/[/\\?%*:|"<>]+/g, "_") })
-					.then(downloadId => new Promise(resolve => {
-						if (request.content) {
-							URL.revokeObjectURL(request.url);
-						}
-						browser.downloads.onChanged.addListener(onChanged);
-
-						function onChanged(event) {
-							if (event.id == downloadId && event.state && event.state.current == "complete") {
-								resolve({});
-								browser.downloads.onChanged.removeListener(onChanged);
-							}
-						}
-					}))
+				return downloadPage(request, { confirmFilename: request.confirmFilename })
 					.catch(() => ({ notSupported: true }));
 			} catch (error) {
 				return Promise.resolve({ notSupported: true });
 			}
 		}
+		if (request.processContent) {
+			processBackgroundTab(request.content, request.url);
+		}
 	});
 
 	return {
@@ -89,6 +77,38 @@ singlefile.core = (() => {
 		}
 	};
 
+	async function processBackgroundTab(content, url) {
+		const options = await singlefile.config.get();
+		options.content = content;
+		options.url = url;
+		options.insertSingleFileComment = true;
+		options.insertFaviconLink = true;
+		options.removeFrames = true;
+		const processor = new (SingleFile.getClass())(options);
+		await processor.initialize();
+		await processor.preparePageData();
+		const page = processor.getPageData();
+		const date = new Date();
+		page.filename = page.title + (options.appendSaveDate ? " (" + date.toISOString().split("T")[0] + " " + date.toLocaleTimeString() + ")" : "") + ".html";
+		page.url = URL.createObjectURL(new Blob([page.content], { type: "text/html" }));
+		return downloadPage(page, options);
+	}
+
+	async function downloadPage(page, options) {
+		return browser.downloads.download({ url: page.url, saveAs: options.confirmFilename, filename: page.filename.replace(/[/\\?%*:|"<>]+/g, "_") })
+			.then(downloadId => new Promise(resolve => {
+				URL.revokeObjectURL(page.url);
+				browser.downloads.onChanged.addListener(onChanged);
+
+				function onChanged(event) {
+					if (event.id == downloadId && event.state && event.state.current == "complete") {
+						resolve({});
+						browser.downloads.onChanged.removeListener(onChanged);
+					}
+				}
+			}));
+	}
+
 	async function processStart(tab, options) {
 		if (!options.removeFrames) {
 			await FrameTree.initialize(tab.id, options);

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

@@ -43,7 +43,8 @@ singlefile.config = (() => {
 		displayInfobar: true,
 		displayStats: false,
 		backgroundSave: true,
-		autoSaveDelay: 1
+		autoSaveDelay: 1,
+		autoSaveUnload: false
 	};
 
 	let pendingUpgradePromise;
@@ -124,6 +125,11 @@ singlefile.config = (() => {
 			config.backgroundSaveDisabled = true;
 			config.autoSaveDelay = 0;
 			config.autoSaveDelayDisabled = true;
+			config.autoSaveUnloadDisabled = true;
+		} else {
+			config.backgroundSaveDisabled = false;
+			config.autoSaveDelayDisabled = false;
+			config.autoSaveUnloadDisabled = false;
 		}
 	}
 

+ 32 - 0
extension/core/content/content-autosave-unload.js

@@ -0,0 +1,32 @@
+/*
+ * Copyright 2018 Gildas Lormeau
+ * contact : gildas.lormeau <at> gmail.com
+ * 
+ * This file is part of SingleFile.
+ *
+ *   SingleFile is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   SingleFile is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with SingleFile.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* global browser, addEventListener, document, location, docHelper */
+
+this.singlefile.autoSave = this.singlefile.autoSave || (() => {
+
+	browser.runtime.sendMessage({ isAutoSaveUnloadEnabled: true }).then(isAutoSaveUnloadEnabled => {
+		if (isAutoSaveUnloadEnabled) {
+			addEventListener("unload", () => browser.runtime.sendMessage({ processContent: true, content: docHelper.serialize(document), url: location.href }));
+		}
+	});
+	return true;
+
+})();

+ 2 - 2
extension/core/content/content.js

@@ -147,9 +147,9 @@ this.singlefile.top = this.singlefile.top || (() => {
 
 	async function downloadPage(page, options) {
 		if (options.backgroundSave) {
-			const response = await browser.runtime.sendMessage({ download: true, url: page.url, saveAs: options.confirmFilename, filename: page.filename });
+			const response = await browser.runtime.sendMessage({ download: true, url: page.url, confirmFilename: options.confirmFilename, filename: page.filename });
 			if (response.notSupported) {
-				const response = await browser.runtime.sendMessage({ download: true, content: page.content, saveAs: options.confirmFilename, filename: page.filename });
+				const response = await browser.runtime.sendMessage({ download: true, content: page.content, confirmFilename: options.confirmFilename, filename: page.filename });
 				if (response.notSupported) {
 					downloadPageFallback(page, options);
 				}

+ 7 - 2
extension/ui/bg/options.js

@@ -44,6 +44,7 @@
 	const displayStatsInput = document.getElementById("displayStatsInput");
 	const backgroundSaveInput = document.getElementById("backgroundSaveInput");
 	const autoSaveDelayInput = document.getElementById("autoSaveDelayInput");
+	const autoSaveUnloadInput = document.getElementById("autoSaveUnloadInput");
 	let pendingSave = Promise.resolve();
 	document.getElementById("resetButton").addEventListener("click", async () => {
 		await bgPage.singlefile.config.reset();
@@ -51,6 +52,7 @@
 		await update();
 	}, false);
 	maxResourceSizeEnabledInput.addEventListener("click", () => maxResourceSizeInput.disabled = !maxResourceSizeEnabledInput.checked, false);
+	autoSaveUnloadInput.addEventListener("click", () => autoSaveDelayInput.disabled = autoSaveUnloadInput.checked, false);
 	document.body.onchange = update;
 	refresh();
 
@@ -79,7 +81,9 @@
 		backgroundSaveInput.checked = config.backgroundSave;
 		backgroundSaveInput.disabled = config.backgroundSaveDisabled;
 		autoSaveDelayInput.value = config.autoSaveDelay;
-		autoSaveDelayInput.disabled = config.autoSaveDelayDisabled;
+		autoSaveDelayInput.disabled = config.autoSaveDelayDisabled || config.autoSaveUnload;
+		autoSaveUnloadInput.checked = config.autoSaveUnload;
+		autoSaveUnloadInput.disabled = config.autoSaveUnloadDisabled;
 	}
 
 	async function update() {
@@ -105,7 +109,8 @@
 			displayInfobar: displayInfobarInput.checked,
 			displayStats: displayStatsInput.checked,
 			backgroundSave: backgroundSaveInput.checked,
-			autoSaveDelay: autoSaveDelayInput.value
+			autoSaveDelay: autoSaveDelayInput.value,
+			autoSaveUnload: autoSaveUnloadInput.checked
 		});
 		await pendingSave;
 		await bgPage.singlefile.ui.update();

+ 16 - 2
extension/ui/bg/ui.js

@@ -115,8 +115,8 @@ singlefile.ui = (() => {
 		onTabActivated(tab);
 	});
 	browser.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
-		const tabsData = await getPersistentTabsData();
-		if (tabsData.autoSaveAll || (tabsData.autoSaveUnpinned && !tab.pinned) || (tabsData[tab.id] && tabsData[tab.id].autoSave)) {
+		const [config, tabsData] = await Promise.all([singlefile.config.get(), getPersistentTabsData()]);
+		if (!config.autoSaveUnload && (tabsData.autoSaveAll || (tabsData.autoSaveUnpinned && !tab.pinned) || (tabsData[tab.id] && tabsData[tab.id].autoSave))) {
 			if (changeInfo.status == "complete") {
 				processTab(tab, { autoSave: true });
 			}
@@ -137,6 +137,9 @@ singlefile.ui = (() => {
 			}
 			onTabError(sender.tab.id);
 		}
+		if (request.isAutoSaveUnloadEnabled) {
+			return isAutoSaveUnloadEnabled(sender.tab.id);
+		}
 	});
 	return { update: refreshContextMenu };
 
@@ -366,6 +369,17 @@ singlefile.ui = (() => {
 		}
 	}
 
+	async function isAutoSaveUnloadEnabled(tabId) {
+		const config = await singlefile.config.get();
+		const autoSaveEnabled = await isAutoSaveEnabled(tabId);
+		return autoSaveEnabled && config.autoSaveUnload;
+	}
+
+	async function isAutoSaveEnabled(tabId) {
+		const tabsData = await getPersistentTabsData();
+		return tabsData.autoSaveAll || tabsData.autoSaveUnpinned || (tabsData[tabId] && tabsData[tabId].autoSave);
+	}
+
 	function getTemporaryTabsData() {
 		if (temporaryTabsData) {
 			return temporaryTabsData;

+ 40 - 38
extension/ui/pages/help.html

@@ -36,25 +36,23 @@
 				<a id="general-notes">Additional notes</a>
 				<ul>
 					<li>Right-click on the SingleFile button and select "Options" to open the options page.</li>
-					<li>To save multiple tabs, select them first and click on the SingleFile button <img src="../resources/icon_16.png"
-						 class="icon">.</li>
+					<li>To save multiple tabs, select them first and click on the SingleFile button
+						<img src="../resources/icon_16.png" class="icon">.</li>
 					<li>Use the context menu to save a tab, a selected content, a frame content, unpinned tabs, selected tabs, or all tabs.
 						The context menu also allows activating automatic page saving of a tab, unpinned tabs, or all tabs.</li>
-					<li>With auto-save active, pages will be automatically saved every time after being loaded. The period of time to wait
-						before saving the page can be configured in the options page.</li>
+					<li>With auto-save active, pages will be automatically saved every time after being loaded. The period of time to wait before
+						saving the page can be configured in the options page.</li>
 				</ul>
 			</li>
 			<li>
 				<a id="options">Options description</a>
 				<p>You can customize SingleFile through the options page. Right-click on SingleFile button
-					<img src="../resources/icon_16.png" class="icon"> in the browser toolbar and select "Options"/"Manage extension" in
-					the context menu to open the options page.</p>
+					<img src="../resources/icon_16.png" class="icon"> in the browser toolbar and select "Options"/"Manage extension" in the context menu to open the options page.</p>
 				<p>Option details :</p>
 				<ul>
 					<li>
 						<span class="option">add entry in the context menu</span>
-						<p>Check this option to display an entry in the context menu of the webpage to save the entire page or the selected
-							part.
+						<p>Check this option to display an entry in the context menu of the webpage to save the entire page or the selected part.
 						</p>
 						<p class="notice">It is recommended to
 							<u>check</u> this option</p>
@@ -62,8 +60,8 @@
 
 					<li>
 						<span class="option">overlay a shadow on the page during processing</span>
-						<p>Check this option to overlay a shadow on the page when SingleFile is retrieving page resources. This reminds you
-							that you should not close the tab.
+						<p>Check this option to overlay a shadow on the page when SingleFile is retrieving page resources. This reminds you that
+							you should not close the tab.
 						</p>
 						<p class="notice">It is recommended to
 							<u>check</u> this option</p>
@@ -78,15 +76,6 @@
 							<u>check</u> this option</p>
 					</li>
 
-					<li>
-						<span class="option">display stats in the console after processing</span>
-						<p>Check this option to display stats about processing in the JavaScript developer tools of your browser. Checking
-							this option may increase the time needed to process a page.
-						</p>
-						<p class="notice">It is recommended to
-							<u>uncheck</u> this option</p>
-					</li>
-
 					<li>
 						<span class="option">append the save date to the file name</span>
 						<p>Check this option to append the save date of the webpage to the file name.
@@ -105,16 +94,16 @@
 
 					<li>
 						<span class="option">compress HTML</span>
-						<p>Check this option to remove all HTML comments, and unneeded spaces or returns. This helps to reduce the size of
-							the file without altering the document.</p>
+						<p>Check this option to remove all HTML comments, and unneeded spaces or returns. This helps to reduce the size of the
+							file without altering the document.</p>
 						<p class="notice">It is recommended to
 							<u>check</u> this option</p>
 					</li>
 
 					<li>
 						<span class="option">remove HTML import</span>
-						<p>Check this option to remove all link elements used to import HTML documents. This can reduce the size of the file
-							without altering the document most of the time.</p>
+						<p>Check this option to remove all link elements used to import HTML documents. This can reduce the size of the file without
+							altering the document most of the time.</p>
 						<p class="notice">It is recommended to
 							<u>check</u> this option</p>
 					</li>
@@ -129,8 +118,8 @@
 
 					<li>
 						<span class="option">remove hidden elements</span>
-						<p>Check this option to remove all hidden elements. This option can considerably reduce the size of the file without
-							altering the document most of the time.</p>
+						<p>Check this option to remove all hidden elements. This option can considerably reduce the size of the file without altering
+							the document most of the time.</p>
 						<p class="notice">It is recommended to
 							<u>check</u> this option</p>
 					</li>
@@ -180,8 +169,8 @@
 
 					<li>
 						<span class="option">save lazy loaded images</span>
-						<p>Check this option to save all the lazy loaded images that are not displayed. This may help to save all the images
-							without scrolling the page. This feature is not guaranteed to work on all sites.</p>
+						<p>Check this option to save all the lazy loaded images that are not displayed. This may help to save all the images without
+							scrolling the page. This feature is not guaranteed to work on all sites.</p>
 						<p class="notice">It is recommended to
 							<u>check</u> this option</p>
 					</li>
@@ -192,6 +181,17 @@
 						</p>
 					</li>
 
+					<li>
+						<span class="option">auto-save on page unload</span>
+						<p>Check this option to auto-save the page when it is unloaded instead of saving the page after it is loaded.</p>
+					</li>
+
+					<li>
+						<span class="option">auto-save wait delay after load (sec.)</span>
+						<p>Specify the time to wait in seconds before saving a page after it is loaded.
+						</p>
+					</li>
+
 					<li>
 						<span class="option">save pages in background</span>
 						<p>Uncheck this option if you get invalid file names like "37bec68b-446a-46a5-8642-19a89c231b46.html" when saving pages.
@@ -201,9 +201,12 @@
 					</li>
 
 					<li>
-						<span class="option">auto-save wait delay (sec.)</span>
-						<p>Specify the time to wait in seconds before saving a page when auto-save is active.
+						<span class="option">display stats in the console after processing</span>
+						<p>Check this option to display stats about processing in the JavaScript developer tools of your browser. Checking this
+							option may increase the time needed to process a page.
 						</p>
+						<p class="notice">It is recommended to
+							<u>uncheck</u> this option</p>
 					</li>
 
 					<li>
@@ -233,20 +236,20 @@
 					<li>
 						All browsers
 						<ul>
-							<li>After installing the extension, if you want to save a tab that was already opened then you need to refresh it
-								first.
+							<li>After installing the extension, if you want to save a tab that was already opened then you need to refresh it first.
 							</li>
 							<li>For security reasons, you cannot save pages hosted on https://chrome.google.com or https://addons.mozilla.org.</li>
+							<li>Frame contents cannot be saved when "auto-save on page unload" option is checked.</li>
 						</ul>
 					</li>
 					<li>
 						Chrome/Opera
 						<ul>
-							<li>You must enable the option "Allow access to file URLs" in the extension page to display the infobar when viewing
-								a saved page, or to save a page stored on the filesystem.</li>
-							<li>If saved pages have file names like "4e5a13fd-6638-4a37-a34b-a3c104ac66b1" instead of the title then it means
-								another extension is conflicting with SingleFile. You can uncheck the option "save pages in background" to circumvent
-								this issue.
+							<li>You must enable the option "Allow access to file URLs" in the extension page to display the infobar when viewing a
+								saved page, or to save a page stored on the filesystem.</li>
+							<li>If saved pages have file names like "4e5a13fd-6638-4a37-a34b-a3c104ac66b1" instead of the title then it means another
+								extension is conflicting with SingleFile. You can uncheck the option "save pages in background" to circumvent this
+								issue.
 							</li>
 						</ul>
 					</li>
@@ -273,8 +276,7 @@
 					<li>If resetting options did not fix the issue, try to disable all other extensions to see if there is a conflict.</li>
 					<li>If there is a conflict then try to determine against which extension(s).</li>
 					<li>Please report the issue
-						<a href="https://github.com/gildas-lormeau/SingleFile/issues">here</a> with a short description describing how to reproduce
-						the issue, Browser version, OS name and version.</li>
+						<a href="https://github.com/gildas-lormeau/SingleFile/issues">here</a> with a short description describing how to reproduce the issue, Browser version, OS name and version.</li>
 				</ul>
 				<p>Suggestions are welcome :)</p>
 			</li>

+ 13 - 6
extension/ui/pages/options.html

@@ -23,10 +23,6 @@
 			<label for="displayInfobarInput">display an infobar when viewing archives</label>
 			<input type="checkbox" id="displayInfobarInput">
 		</div>
-		<div class="option">
-			<label for="displayStatsInput">display stats in the console after processing</label>
-			<input type="checkbox" id="displayStatsInput">
-		</div>
 	</details>
 	<details>
 		<summary>File name</summary>
@@ -97,6 +93,17 @@
 			<input type="number" id="maxResourceSizeInput" min="1">
 		</div>
 	</details>
+	<details>
+		<summary>Auto-save</summary>
+		<div class="option">
+			<label for="autoSaveUnloadInput">auto-save on page unload</label>
+			<input type="checkbox" id="autoSaveUnloadInput">
+		</div>
+		<div class="option">
+			<label for="autoSaveDelayInput">auto-save wait delay after load (sec.)</label>
+			<input type="number" id="autoSaveDelayInput" min="0">
+		</div>
+	</details>
 	<details>
 		<summary>Misc.</summary>
 		<div class="option">
@@ -104,8 +111,8 @@
 			<input type="checkbox" id="backgroundSaveInput">
 		</div>
 		<div class="option">
-			<label for="autoSaveDelayInput">auto-save wait delay (sec.)</label>
-			<input type="number" id="autoSaveDelayInput" min="0">
+			<label for="displayStatsInput">display stats in the console after processing</label>
+			<input type="checkbox" id="displayStatsInput">
 		</div>
 	</details>
 	<div class="option bottom">

+ 24 - 2
manifest.json

@@ -17,13 +17,24 @@
 			"js": [
 				"lib/browser-polyfill/custom-browser-polyfill.js",
 				"extension/index.js",
-				"extension/ui/content/infobar.js",
 				"lib/single-file/doc-helper.js",
 				"lib/single-file/frame-tree/content/frame-tree.js",
 				"extension/core/content/content-frame.js"
 			],
 			"all_frames": true,
 			"match_about_blank": true
+		},
+		{
+			"matches": [
+				"<all_urls>"
+			],
+			"run_at": "document_start",
+			"js": [
+				"extension/ui/content/infobar.js",
+				"extension/core/content/content-autosave-unload.js"
+			],
+			"all_frames": false,
+			"match_about_blank": true
 		}
 	],
 	"background": {
@@ -34,7 +45,18 @@
 			"extension/index.js",
 			"extension/core/bg/config.js",
 			"extension/core/bg/bg.js",
-			"extension/ui/bg/ui.js"
+			"extension/ui/bg/ui.js",
+			"lib/single-file/doc-helper.js",
+			"lib/single-file/base64.js",
+			"lib/single-file/uglifycss.js",
+			"lib/single-file/rules-minifier.js",
+			"lib/single-file/htmlmini.js",
+			"lib/single-file/parse-srcset.js",
+			"lib/single-file/lazy-loader.js",
+			"lib/single-file/serializer.js",
+			"lib/single-file/single-file-core.js",
+			"lib/single-file/single-file-browser.js",
+			"lib/fetch/content/fetch.js"
 		],
 		"persistent": false
 	},