Explorar o código

build background scripts

Gildas %!s(int64=4) %!d(string=hai) anos
pai
achega
7c8501dfc8

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
dist/extension-background.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
dist/single-file-background.js


+ 54 - 0
extension/core/bg/autosave-util.js

@@ -0,0 +1,54 @@
+/*
+ * Copyright 2010-2020 Gildas Lormeau
+ * contact : gildas.lormeau <at> gmail.com
+ * 
+ * This file is part of SingleFile.
+ *
+ *   The code in this file is free software: you can redistribute it and/or 
+ *   modify it under the terms of the GNU Affero General Public License 
+ *   (GNU AGPL) as published by the Free Software Foundation, either version 3
+ *   of the License, or (at your option) any later version.
+ * 
+ *   The code in this file 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 Affero 
+ *   General Public License for more details.
+ *
+ *   As additional permission under GNU AGPL version 3 section 7, you may 
+ *   distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU 
+ *   AGPL normally required by section 4, provided you include this license 
+ *   notice and a URL through which recipients can access the Corresponding 
+ *   Source.
+ */
+
+/* global browser */
+
+import * as config from "./config.js";
+import * as tabsData from "./tabs-data.js";
+
+export {
+	autoSaveIsEnabled,
+	refreshAutoSaveTabs
+};
+
+async function autoSaveIsEnabled(tab) {
+	if (tab) {
+		const [allTabsData, rule] = await Promise.all([tabsData.get(), config.getRule(tab.url)]);
+		return Boolean(allTabsData.autoSaveAll ||
+			(allTabsData.autoSaveUnpinned && !tab.pinned) ||
+			(allTabsData[tab.id] && allTabsData[tab.id].autoSave)) &&
+			(!rule || rule.autoSaveProfile != config.DISABLED_PROFILE_NAME);
+	}
+}
+
+async function refreshAutoSaveTabs() {
+	const tabs = (await browser.tabs.query({}));
+	return Promise.all(tabs.map(async tab => {
+		const [options, autoSaveEnabled] = await Promise.all([config.getOptions(tab.url, true), autoSaveIsEnabled(tab)]);
+		try {
+			await browser.tabs.sendMessage(tab.id, { method: "content.init", autoSaveEnabled, options });
+		} catch (error) {
+			// ignored
+		}
+	}));
+}

+ 8 - 32
extension/core/bg/autosave.js

@@ -21,17 +21,17 @@
  *   Source.
  */
 
-/* global infobar, URL, Blob, XMLHttpRequest */
+/* global browser, infobar, URL, Blob, XMLHttpRequest */
 
 import * as config from "./config.js";
 import * as business from "./business.js";
 import * as companion from "./companion.js";
 import * as downloads from "./downloads.js";
 import * as tabsData from "./tabs-data.js";
-import * as tabs from "./tabs.js";
 import * as ui from "./../../ui/bg/index.js";
 import { getPageData } from "./../../index.js";
 import * as woleet from "./../../lib/woleet/woleet.js";
+import { autoSaveIsEnabled } from "./autosave-util.js";
 
 const pendingMessages = {};
 const replacedTabIds = {};
@@ -40,8 +40,6 @@ export {
 	onMessage,
 	onMessageExternal,
 	onInit,
-	isEnabled,
-	refreshTabs,
 	onTabUpdated,
 	onTabRemoved,
 	onTabDiscarded,
@@ -50,7 +48,7 @@ export {
 
 async function onMessage(message, sender) {
 	if (message.method.endsWith(".init")) {
-		const [options, autoSaveEnabled] = await Promise.all([config.getOptions(sender.tab.url, true), isEnabled(sender.tab)]);
+		const [options, autoSaveEnabled] = await Promise.all([config.getOptions(sender.tab.url, true), autoSaveIsEnabled(sender.tab)]);
 		return { options, autoSaveEnabled, tabId: sender.tab.id, tabIndex: sender.tab.index };
 	}
 	if (message.method.endsWith(".save")) {
@@ -125,39 +123,17 @@ async function onMessageExternal(message, currentTab) {
 		ui.refreshTab(currentTab);
 	}
 	if (message.method == "isAutoSaveEnabled") {
-		return isEnabled(currentTab);
+		return autoSaveIsEnabled(currentTab);
 	}
 }
 
 async function onInit(tab) {
-	const [options, autoSaveEnabled] = await Promise.all([config.getOptions(tab.url, true), isEnabled(tab)]);
+	const [options, autoSaveEnabled] = await Promise.all([config.getOptions(tab.url, true), autoSaveIsEnabled(tab)]);
 	if (options && ((options.autoSaveLoad || options.autoSaveLoadOrUnload) && autoSaveEnabled)) {
 		business.saveTabs([tab], { autoSave: true });
 	}
 }
 
-async function isEnabled(tab) {
-	if (tab) {
-		const [allTabsData, rule] = await Promise.all([tabsData.get(), config.getRule(tab.url)]);
-		return Boolean(allTabsData.autoSaveAll ||
-			(allTabsData.autoSaveUnpinned && !tab.pinned) ||
-			(allTabsData[tab.id] && allTabsData[tab.id].autoSave)) &&
-			(!rule || rule.autoSaveProfile != config.DISABLED_PROFILE_NAME);
-	}
-}
-
-async function refreshTabs() {
-	const allTabs = (await tabs.get({}));
-	return Promise.all(allTabs.map(async tab => {
-		const [options, autoSaveEnabled] = await Promise.all([config.getOptions(tab.url, true), isEnabled(tab)]);
-		try {
-			await tabs.sendMessage(tab.id, { method: "content.init", autoSaveEnabled, options });
-		} catch (error) {
-			// ignored
-		}
-	}));
-}
-
 async function saveContent(message, tab) {
 	const tabId = tab.id;
 	const options = await config.getOptions(tab.url, true);
@@ -210,12 +186,12 @@ async function saveContent(message, tab) {
 						const createTabProperties = { active: true, url: URL.createObjectURL(blob), windowId: tab.windowId };
 						const index = tab.index;
 						try {
-							await tabs.get({ id: tabId });
+							await browser.tabs.get(tabId);
 							createTabProperties.index = index + 1;
 						} catch (error) {
 							createTabProperties.index = index;
 						}
-						tabs.create(createTabProperties);
+						browser.tabs.create(createTabProperties);
 					}
 				}
 				if (pageData.hash) {
@@ -226,7 +202,7 @@ async function saveContent(message, tab) {
 			if (message.taskId) {
 				business.onSaveEnd(message.taskId);
 			} else if (options.autoClose) {
-				tabs.remove(replacedTabIds[tabId] || tabId);
+				browser.tabs.remove(replacedTabIds[tabId] || tabId);
 				delete replacedTabIds[tabId];
 			}
 			if (pageData && pageData.url) {

+ 2 - 5
extension/core/bg/background.html

@@ -8,13 +8,10 @@
 
 <body>
 	<script src="/dist/chrome-browser-polyfill.js"></script>
+	<script src="/dist/single-file-background.js"></script>
+	<script src="/dist/extension-background.js"></script>
 	<script src="/dist/single-file.js"></script>
 	<script src="/dist/infobar.js"></script>
-	<script type="module" src="/extension/core/bg/messages.js"></script>
-	<script type="module" src="/extension/ui/bg/ui-commands.js"></script>
-	<script type="module" src="/extension/lib/single-file/fetch/bg/fetch.js"></script>
-	<script type="module" src="/extension/lib/single-file/frame-tree/bg/frame-tree.js"></script>
-	<script type="module" src="/extension/lib/single-file/lazy/bg/lazy-timeout.js"></script>
 </body>
 
 </html>

+ 4 - 5
extension/core/bg/bookmarks.js

@@ -25,7 +25,6 @@
 
 import * as config from "./config.js";
 import * as business from "./business.js";
-import * as tabs from "./tabs.js";
 
 Promise.resolve().then(enable);
 
@@ -83,7 +82,7 @@ async function update(id, changes) {
 }
 
 async function onCreated(bookmarkId, bookmarkInfo) {
-	const activeTabs = await tabs.get({ lastFocusedWindow: true, active: true });
+	const activeTabs = await browser.tabs.query({ lastFocusedWindow: true, active: true });
 	const options = await config.getOptions(bookmarkInfo.url);
 	if (options.saveCreatedBookmarks) {
 		const bookmarkFolders = await getParentFolders(bookmarkInfo.parentId);
@@ -98,9 +97,9 @@ async function onCreated(bookmarkId, bookmarkInfo) {
 			if (activeTabs.length && activeTabs[0].url == bookmarkInfo.url) {
 				business.saveTabs(activeTabs, { bookmarkId, bookmarkFolders });
 			} else {
-				const allTabs = await tabs.get({});
-				if (allTabs.length) {
-					const tab = allTabs.find(tab => tab.url == bookmarkInfo.url);
+				const tabs = await browser.tabs.query({});
+				if (tabs.length) {
+					const tab = tabs.find(tab => tab.url == bookmarkInfo.url);
 					if (tab) {
 						business.saveTabs([tab], { bookmarkId, bookmarkFolders });
 					} else {

+ 33 - 9
extension/core/bg/business.js

@@ -21,11 +21,12 @@
  *   Source.
  */
 
+/* global browser */
+
 import * as config from "./config.js";
-import * as autosave from "./autosave.js";
+import { autoSaveIsEnabled } from "./autosave-util.js";
 import * as editor from "./editor.js";
 import * as requests from "./requests.js";
-import * as tabs from "./tabs.js";
 import * as ui from "./../../ui/bg/index.js";
 import { injectScript } from "./../../index.js";
 
@@ -45,6 +46,7 @@ const extensionScriptFiles = [
 
 const tasks = [];
 let currentTaskId = 0, maxParallelWorkers;
+ui.setBusiness({ isSavingTab, saveTabs, saveUrls, cancelTab, openEditor, saveSelectedLinks });
 
 export {
 	isSavingTab,
@@ -67,7 +69,7 @@ async function saveSelectedLinks(tab) {
 	const tabOptions = { extensionScriptFiles, tabId: tab.id, tabIndex: tab.index };
 	const scriptsInjected = await injectScript(tab.id, tabOptions);
 	if (scriptsInjected) {
-		const response = await tabs.sendMessage(tab.id, { method: "content.getSelectedLinks" });
+		const response = await browser.tabs.sendMessage(tab.id, { method: "content.getSelectedLinks" });
 		if (response.urls && response.urls.length) {
 			await saveUrls(response.urls);
 		}
@@ -109,7 +111,7 @@ async function saveTabs(tabs, options = {}) {
 			await requests.enableReferrerOnError();
 		}
 		if (options.autoSave) {
-			if (autosave.isEnabled(tab)) {
+			if (autoSaveIsEnabled(tab)) {
 				const taskInfo = addTask({
 					status: TASK_PROCESSING_STATE,
 					tab,
@@ -155,7 +157,7 @@ function addTask(info) {
 }
 
 function openEditor(tab) {
-	tabs.sendMessage(tab.id, { method: "content.openEditor" });
+	browser.tabs.sendMessage(tab.id, { method: "content.openEditor" });
 }
 
 async function initMaxParallelWorkers() {
@@ -180,7 +182,7 @@ async function runTask(taskInfo) {
 	if (!taskInfo.tab.id) {
 		let scriptsInjected;
 		try {
-			const tab = await tabs.createAndWait({ url: taskInfo.tab.url, active: false });
+			const tab = await createTabAndWaitUntilComplete({ url: taskInfo.tab.url, active: false });
 			taskInfo.tab.id = taskInfo.options.tabId = tab.id;
 			taskInfo.tab.index = taskInfo.options.tabIndex = tab.index;
 			ui.onStart(taskInfo.tab.id, INJECT_SCRIPTS_STEP);
@@ -197,7 +199,7 @@ async function runTask(taskInfo) {
 	}
 	taskInfo.options.taskId = taskId;
 	try {
-		await tabs.sendMessage(taskInfo.tab.id, { method: taskInfo.method, options: taskInfo.options });
+		await browser.tabs.sendMessage(taskInfo.tab.id, { method: taskInfo.method, options: taskInfo.options });
 	} catch (error) {
 		if (error && (!error.message || !isIgnoredError(error))) {
 			console.log(error.message ? error.message : error); // eslint-disable-line no-console
@@ -226,12 +228,34 @@ function onSaveEnd(taskId) {
 	const taskInfo = tasks.find(taskInfo => taskInfo.id == taskId);
 	if (taskInfo) {
 		if (taskInfo.options.autoClose && !taskInfo.cancelled) {
-			tabs.remove(taskInfo.tab.id);
+			browser.tabs.remove(taskInfo.tab.id);
 		}
 		taskInfo.done();
 	}
 }
 
+
+async function createTabAndWaitUntilComplete(createProperties) {
+	const tab = await browser.tabs.create(createProperties);
+	return new Promise((resolve, reject) => {
+		browser.tabs.onUpdated.addListener(onTabUpdated);
+		browser.tabs.onRemoved.addListener(onTabRemoved);
+		function onTabUpdated(tabId, changeInfo) {
+			if (tabId == tab.id && changeInfo.status == "complete") {
+				resolve(tab);
+				browser.tabs.onUpdated.removeListener(onTabUpdated);
+				browser.tabs.onRemoved.removeListener(onTabRemoved);
+			}
+		}
+		function onTabRemoved(tabId) {
+			if (tabId == tab.id) {
+				reject(tabId);
+				browser.tabs.onRemoved.removeListener(onTabRemoved);
+			}
+		}
+	});
+}
+
 function setCancelCallback(taskId, cancelCallback) {
 	const taskInfo = tasks.find(taskInfo => taskInfo.id == taskId);
 	if (taskInfo) {
@@ -262,7 +286,7 @@ function getTaskInfo(taskId) {
 function cancel(taskInfo) {
 	const tabId = taskInfo.tab.id;
 	taskInfo.cancelled = true;
-	tabs.sendMessage(tabId, {
+	browser.tabs.sendMessage(tabId, {
 		method: "content.cancelSave",
 		options: {
 			loadDeferredImages: taskInfo.options.loadDeferredImages,

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

@@ -23,7 +23,7 @@
 
 /* global browser, navigator, URL, Blob */
 
-import * as downloads from "./downloads.js";
+import { download } from "./download-util.js";
 import * as tabsData from "./tabs-data.js";
 
 const CURRENT_PROFILE_NAME = "-";
@@ -465,7 +465,7 @@ async function exportConfig() {
 		saveAs: true
 	};
 	try {
-		await downloads.download(downloadInfo, "_");
+		await download(downloadInfo, "_");
 	} finally {
 		URL.revokeObjectURL(url);
 	}

+ 2 - 2
extension/core/bg/devtools.js

@@ -21,7 +21,7 @@
  *   Source.
  */
 
-import * as tabs from "./tabs.js";
+/* global browser */
 
 export {
 	onMessage
@@ -30,7 +30,7 @@ export {
 async function onMessage(message) {
 	if (message.method.endsWith(".resourceCommitted")) {
 		if (message.tabId && message.url && (message.type == "stylesheet" || message.type == "script")) {
-			await tabs.sendMessage(message.tabId, message);
+			await browser.tabs.sendMessage(message.tabId, message);
 		}
 	}
 }

+ 94 - 0
extension/core/bg/download-util.js

@@ -0,0 +1,94 @@
+/*
+ * Copyright 2010-2020 Gildas Lormeau
+ * contact : gildas.lormeau <at> gmail.com
+ * 
+ * This file is part of SingleFile.
+ *
+ *   The code in this file is free software: you can redistribute it and/or 
+ *   modify it under the terms of the GNU Affero General Public License 
+ *   (GNU AGPL) as published by the Free Software Foundation, either version 3
+ *   of the License, or (at your option) any later version.
+ * 
+ *   The code in this file 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 Affero 
+ *   General Public License for more details.
+ *
+ *   As additional permission under GNU AGPL version 3 section 7, you may 
+ *   distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU 
+ *   AGPL normally required by section 4, provided you include this license 
+ *   notice and a URL through which recipients can access the Corresponding 
+ *   Source.
+ */
+
+/* global browser */
+
+const STATE_DOWNLOAD_COMPLETE = "complete";
+const STATE_DOWNLOAD_INTERRUPTED = "interrupted";
+const STATE_ERROR_CANCELED_CHROMIUM = "USER_CANCELED";
+const ERROR_DOWNLOAD_CANCELED_GECKO = "canceled";
+const ERROR_CONFLICT_ACTION_GECKO = "conflictaction prompt not yet implemented";
+const ERROR_INCOGNITO_GECKO = "'incognito'";
+const ERROR_INCOGNITO_GECKO_ALT = "\"incognito\"";
+const ERROR_INVALID_FILENAME_GECKO = "illegal characters";
+const ERROR_INVALID_FILENAME_CHROMIUM = "invalid filename";
+
+export {
+	download
+};
+
+async function download(downloadInfo, replacementCharacter) {
+	let downloadId;
+	try {
+		downloadId = await browser.downloads.download(downloadInfo);
+	} catch (error) {
+		if (error.message) {
+			const errorMessage = error.message.toLowerCase();
+			const invalidFilename = errorMessage.includes(ERROR_INVALID_FILENAME_GECKO) || errorMessage.includes(ERROR_INVALID_FILENAME_CHROMIUM);
+			if (invalidFilename && downloadInfo.filename.startsWith(".")) {
+				downloadInfo.filename = replacementCharacter + downloadInfo.filename;
+				return download(downloadInfo, replacementCharacter);
+			} else if (invalidFilename && downloadInfo.filename.includes(",")) {
+				downloadInfo.filename = downloadInfo.filename.replace(/,/g, replacementCharacter);
+				return download(downloadInfo, replacementCharacter);
+			} else if (invalidFilename && !downloadInfo.filename.match(/^[\x00-\x7F]+$/)) { // eslint-disable-line  no-control-regex
+				downloadInfo.filename = downloadInfo.filename.replace(/[^\x00-\x7F]+/g, replacementCharacter); // eslint-disable-line  no-control-regex
+				return download(downloadInfo, replacementCharacter);
+			} else if ((errorMessage.includes(ERROR_INCOGNITO_GECKO) || errorMessage.includes(ERROR_INCOGNITO_GECKO_ALT)) && downloadInfo.incognito) {
+				delete downloadInfo.incognito;
+				return download(downloadInfo, replacementCharacter);
+			} else if (errorMessage == ERROR_CONFLICT_ACTION_GECKO && downloadInfo.conflictAction) {
+				delete downloadInfo.conflictAction;
+				return download(downloadInfo, replacementCharacter);
+			} else if (errorMessage.includes(ERROR_DOWNLOAD_CANCELED_GECKO)) {
+				return {};
+			} else {
+				throw error;
+			}
+		} else {
+			throw error;
+		}
+	}
+	return new Promise((resolve, reject) => {
+		browser.downloads.onChanged.addListener(onChanged);
+
+		function onChanged(event) {
+			if (event.id == downloadId && event.state) {
+				if (event.state.current == STATE_DOWNLOAD_COMPLETE) {
+					browser.downloads.search({ id: downloadId })
+						.then(downloadItems => resolve({ filename: downloadItems[0] && downloadItems[0].filename }))
+						.catch(() => resolve({}));
+					browser.downloads.onChanged.removeListener(onChanged);
+				}
+				if (event.state.current == STATE_DOWNLOAD_INTERRUPTED) {
+					if (event.error && event.error.current == STATE_ERROR_CANCELED_CHROMIUM) {
+						resolve({});
+					} else {
+						reject(new Error(event.state.current));
+					}
+					browser.downloads.onChanged.removeListener(onChanged);
+				}
+			}
+		}
+	});
+}

+ 6 - 71
extension/core/bg/downloads.js

@@ -28,23 +28,15 @@ import * as bookmarks from "./bookmarks.js";
 import * as companion from "./companion.js";
 import * as business from "./business.js";
 import * as editor from "./editor.js";
-import * as tabs from "./tabs.js";
+import { launchWebAuthFlow, extractAuthCode, promptValue } from "./tabs-util.js";
 import * as ui from "./../../ui/bg/index.js";
 import * as woleet from "./../../lib/woleet/woleet.js";
 import { GDrive } from "./../../lib/gdrive/gdrive.js";
 import { pushGitHub } from "./../../lib/github/github.js";
+import { download } from "./download-util.js";
 
 const partialContents = new Map();
 const MIMETYPE_HTML = "text/html";
-const STATE_DOWNLOAD_COMPLETE = "complete";
-const STATE_DOWNLOAD_INTERRUPTED = "interrupted";
-const STATE_ERROR_CANCELED_CHROMIUM = "USER_CANCELED";
-const ERROR_DOWNLOAD_CANCELED_GECKO = "canceled";
-const ERROR_CONFLICT_ACTION_GECKO = "conflictaction prompt not yet implemented";
-const ERROR_INCOGNITO_GECKO = "'incognito'";
-const ERROR_INCOGNITO_GECKO_ALT = "\"incognito\"";
-const ERROR_INVALID_FILENAME_GECKO = "illegal characters";
-const ERROR_INVALID_FILENAME_CHROMIUM = "invalid filename";
 const CLIENT_ID = "207618107333-3pj2pmelhnl4sf3rpctghs9cean3q8nj.apps.googleusercontent.com";
 const SCOPES = ["https://www.googleapis.com/auth/drive.file"];
 const CONFLICT_ACTION_SKIP = "skip";
@@ -56,7 +48,6 @@ const requestPermissionIdentity = manifest.optional_permissions && manifest.opti
 const gDrive = new GDrive(CLIENT_ID, SCOPES);
 export {
 	onMessage,
-	download,
 	downloadPage,
 	saveToGDrive,
 	saveToGitHub
@@ -165,7 +156,7 @@ async function downloadContent(contents, tab, incognito, message) {
 			if (tab.index != null) {
 				createTabProperties.index = tab.index + 1;
 			}
-			tabs.create(createTabProperties);
+			browser.tabs.create(createTabProperties);
 		}
 	} catch (error) {
 		if (!error.message || error.message != "upload_cancelled") {
@@ -190,9 +181,9 @@ async function getAuthInfo(authOptions, force) {
 		auto: authOptions.extractAuthCode,
 		forceWebAuthFlow: authOptions.forceWebAuthFlow,
 		requestPermissionIdentity,
-		launchWebAuthFlow: options => tabs.launchWebAuthFlow(options),
-		extractAuthCode: authURL => tabs.extractAuthCode(authURL),
-		promptAuthCode: () => tabs.promptValue("Please enter the access code for Google Drive")
+		launchWebAuthFlow: options => launchWebAuthFlow(options),
+		extractAuthCode: authURL => extractAuthCode(authURL),
+		promptAuthCode: () => promptValue("Please enter the access code for Google Drive")
 	};
 	gDrive.setAuthInfo(authInfo, options);
 	if (!authInfo || !authInfo.accessToken || force) {
@@ -291,62 +282,6 @@ async function downloadPage(pageData, options) {
 	}
 }
 
-async function download(downloadInfo, replacementCharacter) {
-	let downloadId;
-	try {
-		downloadId = await browser.downloads.download(downloadInfo);
-	} catch (error) {
-		if (error.message) {
-			const errorMessage = error.message.toLowerCase();
-			const invalidFilename = errorMessage.includes(ERROR_INVALID_FILENAME_GECKO) || errorMessage.includes(ERROR_INVALID_FILENAME_CHROMIUM);
-			if (invalidFilename && downloadInfo.filename.startsWith(".")) {
-				downloadInfo.filename = replacementCharacter + downloadInfo.filename;
-				return download(downloadInfo, replacementCharacter);
-			} else if (invalidFilename && downloadInfo.filename.includes(",")) {
-				downloadInfo.filename = downloadInfo.filename.replace(/,/g, replacementCharacter);
-				return download(downloadInfo, replacementCharacter);
-			} else if (invalidFilename && !downloadInfo.filename.match(/^[\x00-\x7F]+$/)) { // eslint-disable-line  no-control-regex
-				downloadInfo.filename = downloadInfo.filename.replace(/[^\x00-\x7F]+/g, replacementCharacter); // eslint-disable-line  no-control-regex
-				return download(downloadInfo, replacementCharacter);
-			} else if ((errorMessage.includes(ERROR_INCOGNITO_GECKO) || errorMessage.includes(ERROR_INCOGNITO_GECKO_ALT)) && downloadInfo.incognito) {
-				delete downloadInfo.incognito;
-				return download(downloadInfo, replacementCharacter);
-			} else if (errorMessage == ERROR_CONFLICT_ACTION_GECKO && downloadInfo.conflictAction) {
-				delete downloadInfo.conflictAction;
-				return download(downloadInfo, replacementCharacter);
-			} else if (errorMessage.includes(ERROR_DOWNLOAD_CANCELED_GECKO)) {
-				return {};
-			} else {
-				throw error;
-			}
-		} else {
-			throw error;
-		}
-	}
-	return new Promise((resolve, reject) => {
-		browser.downloads.onChanged.addListener(onChanged);
-
-		function onChanged(event) {
-			if (event.id == downloadId && event.state) {
-				if (event.state.current == STATE_DOWNLOAD_COMPLETE) {
-					browser.downloads.search({ id: downloadId })
-						.then(downloadItems => resolve({ filename: downloadItems[0] && downloadItems[0].filename }))
-						.catch(() => resolve({}));
-					browser.downloads.onChanged.removeListener(onChanged);
-				}
-				if (event.state.current == STATE_DOWNLOAD_INTERRUPTED) {
-					if (event.error && event.error.current == STATE_ERROR_CANCELED_CHROMIUM) {
-						resolve({});
-					} else {
-						reject(new Error(event.state.current));
-					}
-					browser.downloads.onChanged.removeListener(onChanged);
-				}
-			}
-		}
-	});
-}
-
 function saveToClipboard(pageData) {
 	const command = "copy";
 	document.addEventListener(command, listener);

+ 3 - 4
extension/core/bg/editor.js

@@ -24,7 +24,6 @@
 /* global browser */
 
 import * as config from "./config.js";
-import * as tabs from "./tabs.js";
 
 const MAX_CONTENT_SIZE = 32 * (1024 * 1024);
 const EDITOR_PAGE_URL = "/extension/ui/pages/editor.html";
@@ -45,7 +44,7 @@ async function open({ tabIndex, content, filename }) {
 	if (tabIndex != null) {
 		createTabProperties.index = tabIndex;
 	}
-	const tab = await tabs.create(createTabProperties);
+	const tab = await browser.tabs.create(createTabProperties);
 	tabsData.set(tab.id, { content, filename });
 }
 
@@ -76,7 +75,7 @@ async function onMessage(message, sender) {
 					message.content = content;
 					message.options = options;
 				}
-				await tabs.sendMessage(tab.id, message);
+				await browser.tabs.sendMessage(tab.id, message);
 			}
 		}
 	}
@@ -98,7 +97,7 @@ async function onMessage(message, sender) {
 		}
 		if (!message.truncated || message.finished) {
 			const updateTabProperties = { url: EDITOR_PAGE_URL };
-			await tabs.update(tab.id, updateTabProperties);
+			await browser.tabs.update(tab.id, updateTabProperties);
 			tabsData.set(tab.id, { url: tab.url, content: contents.join(""), filename: message.filename });
 		}
 	}

+ 2 - 2
extension/core/bg/messages.js

@@ -72,8 +72,8 @@ browser.runtime.onMessage.addListener((message, sender) => {
 });
 if (browser.runtime.onMessageExternal) {
 	browser.runtime.onMessageExternal.addListener(async (message, sender) => {
-		const allTabs = await tabs.get({ currentWindow: true, active: true });
-		const currentTab = allTabs[0];
+		const tabs = await browser.tabs.query({ currentWindow: true, active: true });
+		const currentTab = tabs[0];
 		if (currentTab) {
 			return autosave.onMessageExternal(message, currentTab, sender);
 		} else {

+ 2 - 4
extension/core/bg/tabs-data.js

@@ -23,8 +23,6 @@
 
 /* global browser, setTimeout */
 
-import * as tabs from "./tabs.js";
-
 let persistentData, temporaryData, cleanedUp;
 setTimeout(() => getPersistent().then(tabsData => persistentData = tabsData), 0);
 export {
@@ -86,10 +84,10 @@ async function setPersistent(tabsData) {
 async function cleanup() {
 	if (!cleanedUp) {
 		cleanedUp = true;
-		const allTabs = await tabs.get({ currentWindow: true, highlighted: true });
+		const tabs = await browser.tabs.query({ currentWindow: true, highlighted: true });
 		Object.keys(persistentData).filter(key => {
 			if (key != "autoSaveAll" && key != "autoSaveUnpinned" && key != "profileName") {
-				return !allTabs.find(tab => tab.id == key);
+				return !tabs.find(tab => tab.id == key);
 			}
 		}).forEach(tabId => delete persistentData[tabId]);
 		await browser.storage.local.set({ tabsData: persistentData });

+ 105 - 0
extension/core/bg/tabs-util.js

@@ -0,0 +1,105 @@
+/*
+ * Copyright 2010-2020 Gildas Lormeau
+ * contact : gildas.lormeau <at> gmail.com
+ * 
+ * This file is part of SingleFile.
+ *
+ *   The code in this file is free software: you can redistribute it and/or 
+ *   modify it under the terms of the GNU Affero General Public License 
+ *   (GNU AGPL) as published by the Free Software Foundation, either version 3
+ *   of the License, or (at your option) any later version.
+ * 
+ *   The code in this file 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 Affero 
+ *   General Public License for more details.
+ *
+ *   As additional permission under GNU AGPL version 3 section 7, you may 
+ *   distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU 
+ *   AGPL normally required by section 4, provided you include this license 
+ *   notice and a URL through which recipients can access the Corresponding 
+ *   Source.
+ */
+
+/* global browser */
+
+const pendingPrompts = new Map();
+
+export {
+	onPromptValueResponse,
+	queryTabs,
+	promptValue,
+	extractAuthCode,
+	launchWebAuthFlow
+};
+
+async function onPromptValueResponse(message, sender) {
+	const promptPromise = pendingPrompts.get(sender.tab.id);
+	if (promptPromise) {
+		promptPromise.resolve(message.value);
+		pendingPrompts.delete(sender.tab.id);
+	}
+}
+
+async function queryTabs(options) {
+	const tabs = await browser.tabs.query(options);
+	return tabs.sort((tab1, tab2) => tab1.index - tab2.index);
+}
+
+async function promptValue(promptMessage) {
+	const tabs = await browser.tabs.query({ currentWindow: true, active: true });
+	return new Promise((resolve, reject) => {
+		const selectedTabId = tabs[0].id;
+		browser.tabs.onRemoved.addListener(onTabRemoved);
+		pendingPrompts.set(selectedTabId, { resolve, reject });
+		browser.tabs.sendMessage(selectedTabId, { method: "common.promptValueRequest", promptMessage });
+
+		function onTabRemoved(tabId) {
+			if (tabId == selectedTabId) {
+				pendingPrompts.delete(tabId);
+				browser.tabs.onUpdated.removeListener(onTabRemoved);
+				reject();
+			}
+		}
+	});
+}
+
+function extractAuthCode(authURL) {
+	return new Promise((resolve, reject) => {
+		let authTabId;
+		browser.tabs.onUpdated.addListener(onTabUpdated);
+		browser.tabs.onRemoved.addListener(onTabRemoved);
+
+		function onTabUpdated(tabId, changeInfo) {
+			if (changeInfo && changeInfo.url == authURL) {
+				authTabId = tabId;
+			}
+			if (authTabId == tabId && changeInfo && changeInfo.title && changeInfo.title.startsWith("Success code=")) {
+				browser.tabs.onUpdated.removeListener(onTabUpdated);
+				browser.tabs.onUpdated.removeListener(onTabRemoved);
+				resolve(changeInfo.title.substring(13, changeInfo.title.length - 49));
+			}
+		}
+
+		function onTabRemoved(tabId) {
+			if (tabId == authTabId) {
+				browser.tabs.onUpdated.removeListener(onTabUpdated);
+				browser.tabs.onUpdated.removeListener(onTabRemoved);
+				reject();
+			}
+		}
+	});
+}
+
+async function launchWebAuthFlow(options) {
+	const tab = await browser.tabs.create({ url: options.url, active: true });
+	return new Promise((resolve, reject) => {
+		browser.tabs.onRemoved.addListener(onTabRemoved);
+		function onTabRemoved(tabId) {
+			if (tabId == tab.id) {
+				browser.tabs.onRemoved.removeListener(onTabRemoved);
+				reject(new Error("code_required"));
+			}
+		}
+	});
+}

+ 3 - 120
extension/core/bg/tabs.js

@@ -29,9 +29,9 @@ import * as business from "./business.js";
 import * as editor from "./editor.js";
 import * as tabsData from "./tabs-data.js";
 import * as ui from "./../../ui/bg/index.js";
+import { onPromptValueResponse } from "./tabs-util.js";
 
 const DELAY_MAYBE_INIT = 1500;
-const pendingPrompts = new Map();
 
 browser.tabs.onCreated.addListener(tab => onTabCreated(tab));
 browser.tabs.onActivated.addListener(activeInfo => onTabActivated(activeInfo));
@@ -39,16 +39,7 @@ browser.tabs.onRemoved.addListener(tabId => onTabRemoved(tabId));
 browser.tabs.onUpdated.addListener((tabId, changeInfo) => onTabUpdated(tabId, changeInfo));
 browser.tabs.onReplaced.addListener((addedTabId, removedTabId) => onTabReplaced(addedTabId, removedTabId));
 export {
-	onMessage,
-	get,
-	create,
-	createAndWait,
-	sendMessage,
-	update,
-	remove,
-	promptValue,
-	extractAuthCode,
-	launchWebAuthFlow
+	onMessage
 };
 
 async function onMessage(message, sender) {
@@ -59,11 +50,7 @@ async function onMessage(message, sender) {
 		autosave.onInit(sender.tab);
 	}
 	if (message.method.endsWith(".promptValueResponse")) {
-		const promptPromise = pendingPrompts.get(sender.tab.id);
-		if (promptPromise) {
-			promptPromise.resolve(message.value);
-			pendingPrompts.delete(sender.tab.id);
-		}
+		onPromptValueResponse(message, sender);
 	}
 	if (message.method.endsWith(".getOptions")) {
 		return config.getOptions(message.url);
@@ -73,110 +60,6 @@ async function onMessage(message, sender) {
 	}
 }
 
-function sendMessage(tabId, message, options) {
-	return browser.tabs.sendMessage(tabId, message, options);
-}
-
-function update(tabId, updateProperties) {
-	return browser.tabs.update(tabId, updateProperties);
-}
-
-function remove(tabId) {
-	return browser.tabs.remove(tabId);
-}
-
-function create(createProperties) {
-	return browser.tabs.create(createProperties);
-}
-
-async function createAndWait(createProperties) {
-	const tab = await browser.tabs.create(createProperties);
-	return new Promise((resolve, reject) => {
-		browser.tabs.onUpdated.addListener(onTabUpdated);
-		browser.tabs.onRemoved.addListener(onTabRemoved);
-		function onTabUpdated(tabId, changeInfo) {
-			if (tabId == tab.id && changeInfo.status == "complete") {
-				resolve(tab);
-				browser.tabs.onUpdated.removeListener(onTabUpdated);
-				browser.tabs.onRemoved.removeListener(onTabRemoved);
-			}
-		}
-		function onTabRemoved(tabId) {
-			if (tabId == tab.id) {
-				reject(tabId);
-				browser.tabs.onRemoved.removeListener(onTabRemoved);
-			}
-		}
-	});
-}
-
-async function get(options) {
-	if (options.id) {
-		return browser.tabs.get(options.id);
-	} else {
-		const tabs = await browser.tabs.query(options);
-		return tabs.sort((tab1, tab2) => tab1.index - tab2.index);
-	}
-}
-
-async function promptValue(promptMessage) {
-	const tabs = await browser.tabs.query({ currentWindow: true, active: true });
-	return new Promise((resolve, reject) => {
-		const selectedTabId = tabs[0].id;
-		browser.tabs.onRemoved.addListener(onTabRemoved);
-		pendingPrompts.set(selectedTabId, { resolve, reject });
-		browser.tabs.sendMessage(selectedTabId, { method: "common.promptValueRequest", promptMessage });
-
-		function onTabRemoved(tabId) {
-			if (tabId == selectedTabId) {
-				pendingPrompts.delete(tabId);
-				browser.tabs.onUpdated.removeListener(onTabRemoved);
-				reject();
-			}
-		}
-	});
-}
-
-function extractAuthCode(authURL) {
-	return new Promise((resolve, reject) => {
-		let authTabId;
-		browser.tabs.onUpdated.addListener(onTabUpdated);
-		browser.tabs.onRemoved.addListener(onTabRemoved);
-
-		function onTabUpdated(tabId, changeInfo) {
-			if (changeInfo && changeInfo.url == authURL) {
-				authTabId = tabId;
-			}
-			if (authTabId == tabId && changeInfo && changeInfo.title && changeInfo.title.startsWith("Success code=")) {
-				browser.tabs.onUpdated.removeListener(onTabUpdated);
-				browser.tabs.onUpdated.removeListener(onTabRemoved);
-				resolve(changeInfo.title.substring(13, changeInfo.title.length - 49));
-			}
-		}
-
-		function onTabRemoved(tabId) {
-			if (tabId == authTabId) {
-				browser.tabs.onUpdated.removeListener(onTabUpdated);
-				browser.tabs.onUpdated.removeListener(onTabRemoved);
-				reject();
-			}
-		}
-	});
-}
-
-async function launchWebAuthFlow(options) {
-	const tab = await browser.tabs.create({ url: options.url, active: true });
-	return new Promise((resolve, reject) => {
-		browser.tabs.onRemoved.addListener(onTabRemoved);
-		function onTabRemoved(tabId) {
-			if (tabId == tab.id) {
-				browser.tabs.onRemoved.removeListener(onTabRemoved);
-				reject(new Error("code_required"));
-			}
-		}
-	});
-}
-
 async function onInit(tab, options) {
 	await tabsData.remove(tab.id);
 	const allTabsData = await tabsData.get(tab.id);

+ 26 - 0
extension/lib/single-file/background.js

@@ -0,0 +1,26 @@
+/*
+ * Copyright 2010-2020 Gildas Lormeau
+ * contact : gildas.lormeau <at> gmail.com
+ * 
+ * This file is part of SingleFile.
+ *
+ *   The code in this file is free software: you can redistribute it and/or 
+ *   modify it under the terms of the GNU Affero General Public License 
+ *   (GNU AGPL) as published by the Free Software Foundation, either version 3
+ *   of the License, or (at your option) any later version.
+ * 
+ *   The code in this file 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 Affero 
+ *   General Public License for more details.
+ *
+ *   As additional permission under GNU AGPL version 3 section 7, you may 
+ *   distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU 
+ *   AGPL normally required by section 4, provided you include this license 
+ *   notice and a URL through which recipients can access the Corresponding 
+ *   Source.
+ */
+
+import "./fetch/bg/fetch.js";
+import "./frame-tree/bg/frame-tree.js";
+import "./lazy/bg/lazy-timeout.js";

+ 12 - 3
extension/ui/bg/index.js

@@ -21,9 +21,11 @@
  *   Source.
  */
 
+/* global browser */
+
 import * as button from "./ui-button.js";
 import * as menus from "./ui-menus.js";
-import * as tabs from "./../../core/bg/tabs.js";
+import * as command from "./ui-commands.js";
 
 export {
 	onMessage,
@@ -37,9 +39,16 @@ export {
 	onUploadProgress,
 	onTabCreated,
 	onTabActivated,
-	onInit
+	onInit,
+	setBusiness
 };
 
+function setBusiness(businessApi) {
+	menus.setBusiness(businessApi);
+	button.setBusiness(businessApi);
+	command.setBusiness(businessApi);
+}
+
 function onMessage(message, sender) {
 	if (message.method.endsWith(".refreshMenu")) {
 		return menus.onMessage(message, sender);
@@ -63,7 +72,7 @@ function onStart(tabId, step, autoSave) {
 async function onError(tabId, message, link) {
 	button.onError(tabId);
 	if (message) {
-		await tabs.sendMessage(tabId, { method: "content.error", error: message.toString(), link });
+		await browser.tabs.sendMessage(tabId, { method: "content.error", error: message.toString(), link });
 	}
 }
 

+ 12 - 6
extension/ui/bg/ui-button.js

@@ -23,10 +23,9 @@
 
 /* global browser */
 
-import * as business from "./../../core/bg/business.js";
-import * as tabs from "./../../core/bg/tabs.js";
+import { queryTabs } from "./../../core/bg/tabs-util.js";
 import * as tabsData from "./../../core/bg/tabs-data.js";
-import * as autosave from "./../../core/bg/autosave.js";
+import { autoSaveIsEnabled } from "../../core/bg/autosave-util.js";
 
 const DEFAULT_ICON_PATH = "/extension/ui/resources/icon_128.png";
 const WAIT_ICON_PATH_PREFIX = "/extension/ui/resources/icon_128_wait";
@@ -110,8 +109,10 @@ const BUTTON_STATES = {
 	}
 };
 
+let business;
+
 browser.browserAction.onClicked.addListener(async tab => {
-	const highlightedTabs = await tabs.get({ currentWindow: true, highlighted: true });
+	const highlightedTabs = await queryTabs({ currentWindow: true, highlighted: true });
 	if (highlightedTabs.length <= 1) {
 		toggleSaveTab(tab);
 	} else {
@@ -136,9 +137,14 @@ export {
 	onEdit,
 	onEnd,
 	onCancelled,
-	refreshTab
+	refreshTab,
+	setBusiness
 };
 
+function setBusiness(businessApi) {
+	business = businessApi;
+}
+
 function onMessage(message, sender) {
 	if (message.method.endsWith(".processInit")) {
 		const allTabsData = tabsData.getTemporary(sender.tab.id);
@@ -216,7 +222,7 @@ function onProgress(tabId, index, maxIndex, tooltipMessage) {
 }
 
 async function refreshTab(tab) {
-	const autoSave = await autosave.isEnabled(tab);
+	const autoSave = await autoSaveIsEnabled(tab);
 	const state = getButtonState("default", autoSave);
 	await refresh(tab.id, state);
 }

+ 14 - 5
extension/ui/bg/ui-commands.js

@@ -23,21 +23,30 @@
 
 /* global browser */
 
-import * as tabs from "./../../core/bg/tabs.js";
-import * as business from "./../../core/bg/business.js";
+import { queryTabs } from "./../../core/bg/tabs-util.js";
 
 const commands = browser.commands;
 const BROWSER_COMMANDS_API_SUPPORTED = commands && commands.onCommand && commands.onCommand.addListener;
 
+let business;
+
+export {
+	setBusiness
+};
+
+function setBusiness(businessApi) {
+	business = businessApi;
+}
+
 if (BROWSER_COMMANDS_API_SUPPORTED) {
 	commands.onCommand.addListener(async command => {
 		if (command == "save-selected-tabs") {
-			const highlightedTabs = await tabs.get({ currentWindow: true, highlighted: true });
+			const highlightedTabs = await queryTabs({ currentWindow: true, highlighted: true });
 			business.saveTabs(highlightedTabs, { optionallySelected: true });
 		}
 		if (command == "save-all-tabs") {
-			const allTabs = await tabs.get({ currentWindow: true });
-			business.saveTabs(allTabs);
+			const tabs = await queryTabs({ currentWindow: true });
+			business.saveTabs(tabs);
 		}
 	});
 }

+ 20 - 15
extension/ui/bg/ui-menus.js

@@ -24,10 +24,10 @@
 /* global browser, URL */
 
 import * as config from "./../../core/bg/config.js";
-import * as tabs from "./../../core/bg/tabs.js";
+import { queryTabs } from "./../../core/bg/tabs-util.js";
 import * as tabsData from "./../../core/bg/tabs-data.js";
-import * as business from "./../../core/bg/business.js";
-import * as autosave from "./../../core/bg/autosave.js";
+
+import { refreshAutoSaveTabs } from "./../../core/bg/autosave-util.js";
 import * as button from "./ui-button.js";
 
 const menus = browser.menus || browser.contextMenus;
@@ -91,16 +91,21 @@ const menusTitleState = new Map();
 let contextMenuVisibleState = true;
 let allMenuVisibleState = true;
 let profileIndexes = new Map();
-let menusCreated, pendingRefresh;
+let menusCreated, pendingRefresh, business;
 Promise.resolve().then(initialize);
 export {
 	onMessage,
 	refreshTab as onTabCreated,
 	refreshTab as onTabActivated,
 	refreshTab as onInit,
-	createMenus as refreshTab
+	createMenus as refreshTab,
+	setBusiness
 };
 
+function setBusiness(businessApi) {
+	business = businessApi;
+}
+
 function onMessage(message) {
 	if (message.method.endsWith("refreshMenu")) {
 		createMenus();
@@ -372,7 +377,7 @@ async function createMenus(tab) {
 	menusCreated = true;
 	if (pendingRefresh) {
 		pendingRefresh = false;
-		(await tabs.get({})).forEach(async tab => await refreshTab(tab));
+		(await browser.tabs.query({})).forEach(async tab => await refreshTab(tab));
 	}
 }
 
@@ -403,7 +408,7 @@ async function initialize() {
 				business.saveSelectedLinks(tab);
 			}
 			if (event.menuItemId == MENU_ID_VIEW_PENDINGS) {
-				await tabs.create({ active: true, url: "/extension/ui/pages/pendings.html" });
+				await browser.tabs.create({ active: true, url: "/extension/ui/pages/pendings.html" });
 			}
 			if (event.menuItemId == MENU_ID_SAVE_SELECTED) {
 				business.saveTabs([tab], { selected: true });
@@ -412,16 +417,16 @@ async function initialize() {
 				business.saveTabs([tab], { frameId: event.frameId });
 			}
 			if (event.menuItemId == MENU_ID_SAVE_SELECTED_TABS || event.menuItemId == MENU_ID_BUTTON_SAVE_SELECTED_TABS) {
-				const allTabs = await tabs.get({ currentWindow: true, highlighted: true });
-				business.saveTabs(allTabs);
+				const tabs = await queryTabs({ currentWindow: true, highlighted: true });
+				business.saveTabs(tabs);
 			}
 			if (event.menuItemId == MENU_ID_SAVE_UNPINNED_TABS || event.menuItemId == MENU_ID_BUTTON_SAVE_UNPINNED_TABS) {
-				const allTabs = await tabs.get({ currentWindow: true, pinned: false });
-				business.saveTabs(allTabs);
+				const tabs = await queryTabs({ currentWindow: true, pinned: false });
+				business.saveTabs(tabs);
 			}
 			if (event.menuItemId == MENU_ID_SAVE_ALL_TABS || event.menuItemId == MENU_ID_BUTTON_SAVE_ALL_TABS) {
-				const allTabs = await tabs.get({ currentWindow: true });
-				business.saveTabs(allTabs);
+				const tabs = await queryTabs({ currentWindow: true });
+				business.saveTabs(tabs);
 			}
 			if (event.menuItemId == MENU_ID_AUTO_SAVE_TAB) {
 				const allTabsData = await tabsData.get(tab.id);
@@ -500,14 +505,14 @@ async function initialize() {
 		if (menusCreated) {
 			pendingRefresh = true;
 		} else {
-			(await tabs.get({})).forEach(async tab => await refreshTab(tab));
+			(await browser.tabs.query({})).forEach(async tab => await refreshTab(tab));
 		}
 	}
 }
 
 async function refreshExternalComponents(tab) {
 	const allTabsData = await tabsData.get(tab.id);
-	await autosave.refreshTabs();
+	await refreshAutoSaveTabs();
 	await button.refreshTab(tab);
 	try {
 		await browser.runtime.sendMessage({ method: "options.refresh", profileName: allTabsData.profileName });

+ 14 - 0
rollup.config.dev.js

@@ -109,4 +109,18 @@ export default [{
 		format: "iife",
 		plugins: []
 	}]
+}, {
+	input: ["extension/core/bg/messages.js"],
+	output: [{
+		file: "dist/extension-background.js",
+		format: "iife",
+		plugins: []
+	}]
+}, {
+	input: ["extension/lib/single-file/background.js"],
+	output: [{
+		file: "dist/single-file-background.js",
+		format: "iife",
+		plugins: []
+	}]
 }];

+ 14 - 0
rollup.config.js

@@ -111,4 +111,18 @@ export default [{
 		format: "iife",
 		plugins: [terser()]
 	}]
+}, {
+	input: ["extension/core/bg/messages.js"],
+	output: [{
+		file: "dist/extension-background.js",
+		format: "iife",
+		plugins: [terser()]
+	}]
+}, {
+	input: ["extension/lib/single-file/background.js"],
+	output: [{
+		file: "dist/single-file-background.js",
+		format: "iife",
+		plugins: [terser()]
+	}]
 }];

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio