Procházet zdrojové kódy

add Network panel in the options page
(fix #418, fix #839)

Gildas před 3 roky
rodič
revize
997fa4c7da

+ 47 - 15
_locales/de/messages.json

@@ -303,23 +303,47 @@
 		"message": "Formatvorlagen für alternative Bildschirme löschen",
 		"description": "Options page label: 'remove stylesheets for alternative devices to screens'"
 	},
-	"optionsOtherResourcesSubTitle": {
-		"message": "Weitere Ressourcen",
-		"description": "Options sub-title: 'Other resources'"
+	"optionsNetworkSubTitle": {
+		"message": "Netzwerk",
+		"description": "Options sub-title: 'Network'"
 	},
-	"optionRemoveScripts": {
-		"message": "Scripte entfernen",
-		"description": "Options page label: 'remove scripts'"
+	"optionsBlockedResources": {
+		"message": "blockierte Ressourcen",
+		"description": "Options page label: 'blocked resources'"
 	},
-	"optionRemoveVideo": {
-		"message": "Videoquellen entfernen",
-		"description": "Options page label: 'remove video sources'"
+	"optionsAcceptHeaders": {
+		"message": "Kopfzeile \"Accept\"",
+		"description": "Options page label: '\"Accept\" header'"
 	},
-	"optionRemoveAudio": {
-		"message": "Audioquellen entfernen",
-		"description": "Options page label: 'remove audio sources'"
+	"optionResourceScript": {
+		"message": "Scripts",
+		"description": "Options page label: 'scripts'"
 	},
-	"optionsDestionationSubTitle": {
+	"optionResourceVideo": {
+		"message": "Videos",
+		"description": "Options page label: 'videos'"
+	},
+	"optionResourceAudio": {
+		"message": "Audios",
+		"description": "Options page label: 'audios'"
+	},
+	"optionResourceDocument": {
+		"message": "Dokumente",
+		"description": "Options page label: 'documents'"
+	},
+	"optionResourceFont": {
+		"message": "Schriftarten",
+		"description": "Options page label: 'fonts'"
+	},
+	"optionResourceStylesheet": {
+		"message": "Stylesheets",
+		"description": "Options page label: 'stylesheets'"
+	},
+	"optionResourceImage": {
+		"message": "Bilder",
+		"description": "Options page label: 'images'"
+	},
+	"optionsDestinationSubTitle": {
 		"message": "Datei-Ziel",
 		"description": "Options sub-title: 'Destination'"
 	},
@@ -484,13 +508,21 @@
 		"description": "Options page label: 'display stats in the console after processing'"
 	},
 	"optionSetMaxResourceSize": {
-		"message": "Maximale Größe für eingebettete Ressourcen",
-		"description": "Options page label: 'set a maximum size for embedded resources'"
+		"message": "maximale Größe festlegen",
+		"description": "Options page label: 'set maximum size'"
 	},
 	"optionMaxResourceSize": {
 		"message": "Maximale Größe (MB)",
 		"description": "Options page label: 'maximum size (MB)'"
 	},
+	"optionSetMaxResourceDelay": {
+		"message": "maximale Zeit für den Download festlegen",
+		"description": "Options page label: 'set maximum download time'"
+	},
+	"optionMaxResourceDelay": {
+		"message": "maximale Downloadzeit (s)",
+		"description": "Options page label: 'maximum download time (s)'"
+	},
 	"optionPassReferrerOnError": {
 		"message": "Den \"Referer\"-Header nach einem Fehler bei einer Cross-Origin-Anfrage übergeben",
 		"description": "Options page label: 'pass \"Referer\" header after a cross-origin request error'"

+ 47 - 15
_locales/en/messages.json

@@ -303,23 +303,47 @@
 		"message": "remove stylesheets for alternative devices to screens",
 		"description": "Options page label: 'remove stylesheets for alternative devices to screens'"
 	},
-	"optionsOtherResourcesSubTitle": {
-		"message": "Other resources",
-		"description": "Options sub-title: 'Other resources'"
+	"optionsNetworkSubTitle": {
+		"message": "Network",
+		"description": "Options sub-title: 'Network'"
 	},
-	"optionRemoveScripts": {
-		"message": "remove scripts",
-		"description": "Options page label: 'remove scripts'"
+	"optionsBlockedResources": {
+		"message": "blocked resources",
+		"description": "Options page label: 'blocked resources'"
 	},
-	"optionRemoveVideo": {
-		"message": "remove video sources",
-		"description": "Options page label: 'remove video sources'"
+	"optionsAcceptHeaders": {
+		"message": "\"Accept\" header",
+		"description": "Options page label: '\"Accept\" header'"
 	},
-	"optionRemoveAudio": {
-		"message": "remove audio sources",
-		"description": "Options page label: 'remove audio sources'"
+	"optionResourceScript": {
+		"message": "scripts",
+		"description": "Options page label: 'scripts'"
 	},
-	"optionsDestionationSubTitle": {
+	"optionResourceVideo": {
+		"message": "videos",
+		"description": "Options page label: 'videos'"
+	},
+	"optionResourceAudio": {
+		"message": "audios",
+		"description": "Options page label: 'audios'"
+	},
+	"optionResourceDocument": {
+		"message": "documents",
+		"description": "Options page label: 'documents'"
+	},
+	"optionResourceFont": {
+		"message": "fonts",
+		"description": "Options page label: 'fonts'"
+	},
+	"optionResourceStylesheet": {
+		"message": "stylesheets",
+		"description": "Options page label: 'stylesheets'"
+	},
+	"optionResourceImage": {
+		"message": "images",
+		"description": "Options page label: 'images'"
+	},
+	"optionsDestinationSubTitle": {
 		"message": "Destination",
 		"description": "Options sub-title: 'Destination'"
 	},
@@ -484,13 +508,21 @@
 		"description": "Options page label: 'display stats in the console after processing'"
 	},
 	"optionSetMaxResourceSize": {
-		"message": "set a maximum size for embedded resources",
-		"description": "Options page label: 'set a maximum size for embedded resources'"
+		"message": "set maximum size",
+		"description": "Options page label: 'set maximum size'"
 	},
 	"optionMaxResourceSize": {
 		"message": "maximum size (MB)",
 		"description": "Options page label: 'maximum size (MB)'"
 	},
+	"optionSetMaxResourceDelay": {
+		"message": "set maximum download time",
+		"description": "Options page label: 'set maximum download time'"
+	},
+	"optionMaxResourceDelay": {
+		"message": "maximum download time (s)",
+		"description": "Options page label: 'maximum download time (s)'"
+	},
 	"optionPassReferrerOnError": {
 		"message": "pass \"Referer\" header after a cross-origin request error",
 		"description": "Options page label: 'pass \"Referer\" header after a cross-origin request error'"

+ 47 - 15
_locales/es/messages.json

@@ -303,23 +303,47 @@
 		"message": "eliminar hojas de estilo para dispositivos alternativos a las pantallas",
 		"description": "Options page label: 'remove stylesheets for alternative devices to screens'"
 	},
-	"optionsOtherResourcesSubTitle": {
-		"message": "Otros recursos",
-		"description": "Options sub-title: 'Other resources'"
+	"optionsNetworkSubTitle": {
+		"message": "Red",
+		"description": "Options sub-title: 'Network'"
 	},
-	"optionRemoveScripts": {
-		"message": "eliminar scripts",
-		"description": "Options page label: 'remove scripts'"
+	"optionsBlockedResources": {
+		"message": "recursos bloqueados",
+		"description": "Options page label: 'blocked resources'"
 	},
-	"optionRemoveVideo": {
-		"message": "eliminar fuentes de vídeo",
-		"description": "Options page label: 'remove video sources'"
+	"optionsAcceptHeaders": {
+		"message": "Cabecera \"Accept\"",
+		"description": "Options page label: '\"Accept\" header'"
 	},
-	"optionRemoveAudio": {
-		"message": "eliminar fuentes de audio",
-		"description": "Options page label: 'remove audio sources'"
+	"optionResourceScript": {
+		"message": "scripts",
+		"description": "Options page label: 'scripts'"
 	},
-	"optionsDestionationSubTitle": {
+	"optionResourceVideo": {
+		"message": "videos",
+		"description": "Options page label: 'videos'"
+	},
+	"optionResourceAudio": {
+		"message": "audios",
+		"description": "Options page label: 'audios'"
+	},
+	"optionResourceDocument": {
+		"message": "documentos",
+		"description": "Options page label: 'documents'"
+	},
+	"optionResourceFont": {
+		"message": "fuentes",
+		"description": "Options page label: 'fonts'"
+	},
+	"optionResourceStylesheet": {
+		"message": "hojas de estilo",
+		"description": "Options page label: 'stylesheets'"
+	},
+	"optionResourceImage": {
+		"message": "imágenes",
+		"description": "Options page label: 'images'"
+	},
+	"optionsDestinationSubTitle": {
 		"message": "Destino",
 		"description": "Options sub-title: 'Destination'"
 	},
@@ -484,13 +508,21 @@
 		"description": "Options page label: 'display stats in the console after processing'"
 	},
 	"optionSetMaxResourceSize": {
-		"message": "establecer un tamaño máximo para recursos incrustados",
-		"description": "Options page label: 'set a maximum size for embedded resources'"
+		"message": "establecer el tamaño máximo",
+		"description": "Options page label: 'set maximum size'"
 	},
 	"optionMaxResourceSize": {
 		"message": "tamaño máximo (MB)",
 		"description": "Options page label: 'maximum size (MB)'"
 	},
+	"optionSetMaxResourceDelay": {
+		"message": "establecer el tiempo máximo de descarga",
+		"description": "Options page label: 'set maximum download time'"
+	},
+	"optionMaxResourceDelay": {
+		"message": "tiempo máximo de descarga (s)",
+		"description": "Options page label: 'maximum download time (s)'"
+	},
 	"optionPassReferrerOnError": {
 		"message": "pasar el encabezado \"Referer\" después de un error de solicitud de origen cruzado",
 		"description": "Options page label: 'pass \"Referer\" header after a cross-origin request error'"

+ 47 - 15
_locales/fr/messages.json

@@ -303,23 +303,47 @@
 		"message": "supprimer les feuilles de styles pour les appareils autres que des écrans",
 		"description": "Options page label: 'remove stylesheets for alternative devices to screens'"
 	},
-	"optionsOtherResourcesSubTitle": {
-		"message": "Autres ressources",
-		"description": "Options sub-title: 'Other resources'"
+	"optionsNetworkSubTitle": {
+		"message": "Réseau",
+		"description": "Options sub-title: 'Network'"
 	},
-	"optionRemoveScripts": {
-		"message": "supprimer les scripts",
-		"description": "Options page label: 'remove scripts'"
+	"optionsBlockedResources": {
+		"message": "ressources bloquées",
+		"description": "Options page label: 'blocked resources'"
 	},
-	"optionRemoveVideo": {
-		"message": "supprimer les sources video",
-		"description": "Options page label: 'remove video sources'"
+	"optionsAcceptHeaders": {
+		"message": "en-tête \"Accept\"",
+		"description": "Options page label: '\"Accept\" header'"
 	},
-	"optionRemoveAudio": {
-		"message": "supprimer les sources audio",
-		"description": "Options page label: 'remove audio sources'"
+	"optionResourceScript": {
+		"message": "scripts",
+		"description": "Options page label: 'scripts'"
 	},
-	"optionsDestionationSubTitle": {
+	"optionResourceVideo": {
+		"message": "vidéos",
+		"description": "Options page label: 'videos'"
+	},
+	"optionResourceAudio": {
+		"message": "audios",
+		"description": "Options page label: 'audios'"
+	},
+	"optionResourceDocument": {
+		"message": "documents",
+		"description": "Options page label: 'documents'"
+	},
+	"optionResourceFont": {
+		"message": "polices de caractères",
+		"description": "Options page label: 'fonts'"
+	},
+	"optionResourceStylesheet": {
+		"message": "feuilles de style",
+		"description": "Options page label: 'stylesheets'"
+	},
+	"optionResourceImage": {
+		"message": "images",
+		"description": "Options page label: 'images'"
+	},
+	"optionsDestinationSubTitle": {
 		"message": "Destination",
 		"description": "Options sub-title: 'Destination'"
 	},
@@ -484,13 +508,21 @@
 		"description": "Options page label: 'display stats in the console after processing'"
 	},
 	"optionSetMaxResourceSize": {
-		"message": "définir une taille maximale pour les ressources embarquées",
-		"description": "Options page label: 'set a maximum size for embedded resources'"
+		"message": "définir la taille maximale",
+		"description": "Options page label: 'set maximum size'"
 	},
 	"optionMaxResourceSize": {
 		"message": "taille maximale (Mo)",
 		"description": "Options page label: 'maximum size (MB)'"
 	},
+	"optionSetMaxResourceDelay": {
+		"message": "définir la durée maximale de téléchargement",
+		"description": "Options page label: 'set maximum download time'"
+	},
+	"optionMaxResourceDelay": {
+		"message": "durée maximale de téléchargement (s)",
+		"description": "Options page label: 'maximum download time (s)'"
+	},
 	"optionPassReferrerOnError": {
 		"message": "passer l'en-tête \"Referer\" après une erreur de requête multi-origine",
 		"description": "Options page label: 'pass \"Referer\" header after a cross-origin request error'"

+ 47 - 15
_locales/it/messages.json

@@ -303,23 +303,47 @@
 		"message": "rimuovi stili non utilizzati per per schermi dispositivi alternativi",
 		"description": "Options page label: 'remove stylesheets for alternative devices to screens'"
 	},
-	"optionsOtherResourcesSubTitle": {
-		"message": "Altre risorse",
-		"description": "Options sub-title: 'Other resources'"
+	"optionsNetworkSubTitle": {
+		"message": "Network",
+		"description": "Options sub-title: 'Network'"
 	},
-	"optionRemoveScripts": {
-		"message": "rimuovi script",
-		"description": "Options page label: 'remove scripts'"
+	"optionsBlockedResources": {
+		"message": "blocked resources",
+		"description": "Options page label: 'blocked resources'"
 	},
-	"optionRemoveVideo": {
-		"message": "rimuovi sorgenti video",
-		"description": "Options page label: 'remove video sources'"
+	"optionsAcceptHeaders": {
+		"message": "\"Accept\" header",
+		"description": "Options page label: '\"Accept\" header'"
 	},
-	"optionRemoveAudio": {
-		"message": "rimuovi sorgenti audio",
-		"description": "Options page label: 'remove audio sources'"
+	"optionResourceScript": {
+		"message": "scripts",
+		"description": "Options page label: 'scripts'"
 	},
-	"optionsDestionationSubTitle": {
+	"optionResourceVideo": {
+		"message": "videos",
+		"description": "Options page label: 'videos'"
+	},
+	"optionResourceAudio": {
+		"message": "audios",
+		"description": "Options page label: 'audios'"
+	},
+	"optionResourceDocument": {
+		"message": "documents",
+		"description": "Options page label: 'documents'"
+	},
+	"optionResourceFont": {
+		"message": "fonts",
+		"description": "Options page label: 'fonts'"
+	},
+	"optionResourceStylesheet": {
+		"message": "stylesheets",
+		"description": "Options page label: 'stylesheets'"
+	},
+	"optionResourceImage": {
+		"message": "images",
+		"description": "Options page label: 'images'"
+	},
+	"optionsDestinationSubTitle": {
 		"message": "Destinazione",
 		"description": "Options sub-title: 'Destination'"
 	},
@@ -484,13 +508,21 @@
 		"description": "Options page label: 'display stats in the console after processing'"
 	},
 	"optionSetMaxResourceSize": {
-		"message": "imposta ina grandezza massima per le risorse integrate",
-		"description": "Options page label: 'set a maximum size for embedded resources'"
+		"message": "imposta dimensione massima",
+		"description": "Options page label: 'set maximum size'"
 	},
 	"optionMaxResourceSize": {
 		"message": "dimensione massima (MB)",
 		"description": "Options page label: 'maximum size (MB)'"
 	},
+	"optionSetMaxResourceDelay": {
+		"message": "impostare tempo massimo di download",
+		"description": "Options page label: 'set maximum download time'"
+	},
+	"optionMaxResourceDelay": {
+		"message": "tempo massimo di download (s)",
+		"description": "Options page label: 'maximum download time (s)'"
+	},
 	"optionPassReferrerOnError": {
 		"message": "passa header \"Referer\" dopo un errore di richiesta cross-origin",
 		"description": "Options page label: 'pass \"Referer\" header after a cross-origin request error'"

+ 47 - 15
_locales/ja/messages.json

@@ -303,23 +303,47 @@
 		"message": "代替デバイス用のスタイルシートを画面に表示しない",
 		"description": "Options page label: 'remove stylesheets for alternative devices to screens'"
 	},
-	"optionsOtherResourcesSubTitle": {
-		"message": "その他の要素",
-		"description": "Options sub-title: 'Other resources'"
+	"optionsNetworkSubTitle": {
+		"message": "Network",
+		"description": "Options sub-title: 'Network'"
 	},
-	"optionRemoveScripts": {
-		"message": "スクリプトを削除する",
-		"description": "Options page label: 'remove scripts'"
+	"optionsBlockedResources": {
+		"message": "blocked resources",
+		"description": "Options page label: 'blocked resources'"
 	},
-	"optionRemoveVideo": {
-		"message": "ビデオソースを削除する",
-		"description": "Options page label: 'remove video sources'"
+	"optionsAcceptHeaders": {
+		"message": "\"Accept\" header",
+		"description": "Options page label: '\"Accept\" header'"
 	},
-	"optionRemoveAudio": {
-		"message": "オーディオソースを削除する",
-		"description": "Options page label: 'remove audio sources'"
+	"optionResourceScript": {
+		"message": "scripts",
+		"description": "Options page label: 'scripts'"
 	},
-	"optionsDestionationSubTitle": {
+	"optionResourceVideo": {
+		"message": "videos",
+		"description": "Options page label: 'videos'"
+	},
+	"optionResourceAudio": {
+		"message": "audios",
+		"description": "Options page label: 'audios'"
+	},
+	"optionResourceDocument": {
+		"message": "documents",
+		"description": "Options page label: 'documents'"
+	},
+	"optionResourceFont": {
+		"message": "fonts",
+		"description": "Options page label: 'fonts'"
+	},
+	"optionResourceStylesheet": {
+		"message": "stylesheets",
+		"description": "Options page label: 'stylesheets'"
+	},
+	"optionResourceImage": {
+		"message": "images",
+		"description": "Options page label: 'images'"
+	},
+	"optionsDestinationSubTitle": {
 		"message": "Destination",
 		"description": "Options sub-title: 'Destination'"
 	},
@@ -484,13 +508,21 @@
 		"description": "Options page label: 'display stats in the console after processing'"
 	},
 	"optionSetMaxResourceSize": {
-		"message": "埋め込み要素の最大サイズを設定する",
-		"description": "Options page label: 'set a maximum size for embedded resources'"
+		"message": "set maximum size",
+		"description": "Options page label: 'set maximum size'"
 	},
 	"optionMaxResourceSize": {
 		"message": "最大サイズ(MB)",
 		"description": "Options page label: 'maximum size (MB)'"
 	},
+	"optionSetMaxResourceDelay": {
+		"message": "set maximum download time",
+		"description": "Options page label: 'set maximum download time'"
+	},
+	"optionMaxResourceDelay": {
+		"message": "maximum download time (s)",
+		"description": "Options page label: 'maximum download time (s)'"
+	},
 	"optionPassReferrerOnError": {
 		"message": "pass \"Referer\" header after a cross-origin request error",
 		"description": "Options page label: 'pass \"Referer\" header after a cross-origin request error'"

+ 47 - 15
_locales/pl/messages.json

@@ -303,23 +303,47 @@
 		"message": "usuwaj arkusze stylów dla urządzeń innych niż ekrany",
 		"description": "Options page label: 'remove stylesheets for alternative devices to screens'"
 	},
-	"optionsOtherResourcesSubTitle": {
-		"message": "Inne zasoby",
-		"description": "Options sub-title: 'Other resources'"
+	"optionsNetworkSubTitle": {
+		"message": "Network",
+		"description": "Options sub-title: 'Network'"
 	},
-	"optionRemoveScripts": {
-		"message": "usuwaj skrypty",
-		"description": "Options page label: 'remove scripts'"
+	"optionsBlockedResources": {
+		"message": "blocked resources",
+		"description": "Options page label: 'blocked resources'"
 	},
-	"optionRemoveVideo": {
-		"message": "usuwaj źródła wideo",
-		"description": "Options page label: 'remove video sources'"
+	"optionsAcceptHeaders": {
+		"message": "\"Accept\" header",
+		"description": "Options page label: '\"Accept\" header'"
 	},
-	"optionRemoveAudio": {
-		"message": "usuwaj źródła audio",
-		"description": "Options page label: 'remove audio sources'"
+	"optionResourceScript": {
+		"message": "scripts",
+		"description": "Options page label: 'scripts'"
 	},
-	"optionsDestionationSubTitle": {
+	"optionResourceVideo": {
+		"message": "videos",
+		"description": "Options page label: 'videos'"
+	},
+	"optionResourceAudio": {
+		"message": "audios",
+		"description": "Options page label: 'audios'"
+	},
+	"optionResourceDocument": {
+		"message": "documents",
+		"description": "Options page label: 'documents'"
+	},
+	"optionResourceFont": {
+		"message": "fonts",
+		"description": "Options page label: 'fonts'"
+	},
+	"optionResourceStylesheet": {
+		"message": "stylesheets",
+		"description": "Options page label: 'stylesheets'"
+	},
+	"optionResourceImage": {
+		"message": "images",
+		"description": "Options page label: 'images'"
+	},
+	"optionsDestinationSubTitle": {
 		"message": "Cel",
 		"description": "Options sub-title: 'Destination'"
 	},
@@ -484,13 +508,21 @@
 		"description": "Options page label: 'display stats in the console after processing'"
 	},
 	"optionSetMaxResourceSize": {
-		"message": "ustaw maksymalny rozmiar dla zasobów osadzonych",
-		"description": "Options page label: 'set a maximum size for embedded resources'"
+		"message": "set maximum size",
+		"description": "Options page label: 'set maximum size'"
 	},
 	"optionMaxResourceSize": {
 		"message": "maksymalny rozmiar (MB)",
 		"description": "Options page label: 'maximum size (MB)'"
 	},
+	"optionSetMaxResourceDelay": {
+		"message": "set maximum download time",
+		"description": "Options page label: 'set maximum download time'"
+	},
+	"optionMaxResourceDelay": {
+		"message": "maximum download time (s)",
+		"description": "Options page label: 'maximum download time (s)'"
+	},
 	"optionPassReferrerOnError": {
 		"message": "przekazuj nagłówek \"Referer\" po błędzie żądania między źródłami",
 		"description": "Options page label: 'pass \"Referer\" header after a cross-origin request error'"

+ 47 - 15
_locales/pt_br/messages.json

@@ -303,23 +303,47 @@
 		"message": "remover folhas de estilo para dispositivos além de telas",
 		"description": "Options page label: 'remove stylesheets for alternative devices to screens'"
 	},
-	"optionsOtherResourcesSubTitle": {
-		"message": "Outros recursos",
-		"description": "Options sub-title: 'Other resources'"
+	"optionsNetworkSubTitle": {
+		"message": "Network",
+		"description": "Options sub-title: 'Network'"
 	},
-	"optionRemoveScripts": {
-		"message": "remover scripts",
-		"description": "Options page label: 'remove scripts'"
+	"optionsBlockedResources": {
+		"message": "blocked resources",
+		"description": "Options page label: 'blocked resources'"
 	},
-	"optionRemoveVideo": {
-		"message": "remover fontes de vídeo",
-		"description": "Options page label: 'remove video sources'"
+	"optionsAcceptHeaders": {
+		"message": "\"Accept\" header",
+		"description": "Options page label: '\"Accept\" header'"
 	},
-	"optionRemoveAudio": {
-		"message": "remover fontes de áudio",
-		"description": "Options page label: 'remove audio sources'"
+	"optionResourceScript": {
+		"message": "scripts",
+		"description": "Options page label: 'scripts'"
 	},
-	"optionsDestionationSubTitle": {
+	"optionResourceVideo": {
+		"message": "videos",
+		"description": "Options page label: 'videos'"
+	},
+	"optionResourceAudio": {
+		"message": "audios",
+		"description": "Options page label: 'audios'"
+	},
+	"optionResourceDocument": {
+		"message": "documents",
+		"description": "Options page label: 'documents'"
+	},
+	"optionResourceFont": {
+		"message": "fonts",
+		"description": "Options page label: 'fonts'"
+	},
+	"optionResourceStylesheet": {
+		"message": "stylesheets",
+		"description": "Options page label: 'stylesheets'"
+	},
+	"optionResourceImage": {
+		"message": "images",
+		"description": "Options page label: 'images'"
+	},
+	"optionsDestinationSubTitle": {
 		"message": "Destino",
 		"description": "Options sub-title: 'Destination'"
 	},
@@ -484,13 +508,21 @@
 		"description": "Options page label: 'display stats in the console after processing'"
 	},
 	"optionSetMaxResourceSize": {
-		"message": "estabelecer um tamanho máximo para os recursos integrados",
-		"description": "Options page label: 'set a maximum size for embedded resources'"
+		"message": "set maximum size",
+		"description": "Options page label: 'set maximum size'"
 	},
 	"optionMaxResourceSize": {
 		"message": "tamanho máximo (MB)",
 		"description": "Options page label: 'maximum size (MB)'"
 	},
+	"optionSetMaxResourceDelay": {
+		"message": "set maximum download time",
+		"description": "Options page label: 'set maximum download time'"
+	},
+	"optionMaxResourceDelay": {
+		"message": "maximum download time (s)",
+		"description": "Options page label: 'maximum download time (s)'"
+	},
 	"optionPassReferrerOnError": {
 		"message": "passar \"Referer\" header após um erro de pedido de origem cruzada",
 		"description": "Options page label: 'pass \"Referer\" header after a cross-origin request error'"

+ 47 - 15
_locales/ru/messages.json

@@ -303,23 +303,47 @@
 		"message": "удалить таблицы стилей для экранов альтернативных устройств",
 		"description": "Options page label: 'remove stylesheets for alternative devices to screens'"
 	},
-	"optionsOtherResourcesSubTitle": {
-		"message": "Другие ресурсы",
-		"description": "Options sub-title: 'Other resources'"
+	"optionsNetworkSubTitle": {
+		"message": "Network",
+		"description": "Options sub-title: 'Network'"
 	},
-	"optionRemoveScripts": {
-		"message": "удалить скрипты",
-		"description": "Options page label: 'remove scripts'"
+	"optionsBlockedResources": {
+		"message": "blocked resources",
+		"description": "Options page label: 'blocked resources'"
 	},
-	"optionRemoveVideo": {
-		"message": "удалить источники видео",
-		"description": "Options page label: 'remove video sources'"
+	"optionsAcceptHeaders": {
+		"message": "\"Accept\" header",
+		"description": "Options page label: '\"Accept\" header'"
 	},
-	"optionRemoveAudio": {
-		"message": "удалить источники аудио",
-		"description": "Options page label: 'remove audio sources'"
+	"optionResourceScript": {
+		"message": "scripts",
+		"description": "Options page label: 'scripts'"
 	},
-	"optionsDestionationSubTitle": {
+	"optionResourceVideo": {
+		"message": "videos",
+		"description": "Options page label: 'videos'"
+	},
+	"optionResourceAudio": {
+		"message": "audios",
+		"description": "Options page label: 'audios'"
+	},
+	"optionResourceDocument": {
+		"message": "documents",
+		"description": "Options page label: 'documents'"
+	},
+	"optionResourceFont": {
+		"message": "fonts",
+		"description": "Options page label: 'fonts'"
+	},
+	"optionResourceStylesheet": {
+		"message": "stylesheets",
+		"description": "Options page label: 'stylesheets'"
+	},
+	"optionResourceImage": {
+		"message": "images",
+		"description": "Options page label: 'images'"
+	},
+	"optionsDestinationSubTitle": {
 		"message": "Место назначения",
 		"description": "Options sub-title: 'Destination'"
 	},
@@ -484,13 +508,21 @@
 		"description": "Options page label: 'display stats in the console after processing'"
 	},
 	"optionSetMaxResourceSize": {
-		"message": "установить максимальный размер для встроенных ресурсов",
-		"description": "Options page label: 'set a maximum size for embedded resources'"
+		"message": "set maximum size",
+		"description": "Options page label: 'set maximum size'"
 	},
 	"optionMaxResourceSize": {
 		"message": "максимальный размер (МБ)",
 		"description": "Options page label: 'maximum size (MB)'"
 	},
+	"optionSetMaxResourceDelay": {
+		"message": "set maximum download time",
+		"description": "Options page label: 'set maximum download time'"
+	},
+	"optionMaxResourceDelay": {
+		"message": "maximum download time (s)",
+		"description": "Options page label: 'maximum download time (s)'"
+	},
 	"optionPassReferrerOnError": {
 		"message": "пропустить заголовок \"Referer\" после ошибки запроса перекрёстного источника",
 		"description": "Options page label: 'pass \"Referer\" header after a cross-origin request error'"

+ 47 - 15
_locales/uk/messages.json

@@ -303,23 +303,47 @@
 		"message": "видалити стилі для екранів альтернативних пристроїв",
 		"description": "Options page label: 'remove stylesheets for alternative devices to screens'"
 	},
-	"optionsOtherResourcesSubTitle": {
-		"message": "Інші ресурси",
-		"description": "Options sub-title: 'Other resources'"
+	"optionsNetworkSubTitle": {
+		"message": "Network",
+		"description": "Options sub-title: 'Network'"
 	},
-	"optionRemoveScripts": {
-		"message": "видалити скрипти",
-		"description": "Options page label: 'remove scripts'"
+	"optionsBlockedResources": {
+		"message": "blocked resources",
+		"description": "Options page label: 'blocked resources'"
 	},
-	"optionRemoveVideo": {
-		"message": "видалити джерела відео",
-		"description": "Options page label: 'remove video sources'"
+	"optionsAcceptHeaders": {
+		"message": "\"Accept\" header",
+		"description": "Options page label: '\"Accept\" header'"
 	},
-	"optionRemoveAudio": {
-		"message": "видалити джерела аудіо",
-		"description": "Options page label: 'remove audio sources'"
+	"optionResourceScript": {
+		"message": "scripts",
+		"description": "Options page label: 'scripts'"
 	},
-	"optionsDestionationSubTitle": {
+	"optionResourceVideo": {
+		"message": "videos",
+		"description": "Options page label: 'videos'"
+	},
+	"optionResourceAudio": {
+		"message": "audios",
+		"description": "Options page label: 'audios'"
+	},
+	"optionResourceDocument": {
+		"message": "documents",
+		"description": "Options page label: 'documents'"
+	},
+	"optionResourceFont": {
+		"message": "fonts",
+		"description": "Options page label: 'fonts'"
+	},
+	"optionResourceStylesheet": {
+		"message": "stylesheets",
+		"description": "Options page label: 'stylesheets'"
+	},
+	"optionResourceImage": {
+		"message": "images",
+		"description": "Options page label: 'images'"
+	},
+	"optionsDestinationSubTitle": {
 		"message": "Destination",
 		"description": "Options sub-title: 'Destination'"
 	},
@@ -484,13 +508,21 @@
 		"description": "Options page label: 'display stats in the console after processing'"
 	},
 	"optionSetMaxResourceSize": {
-		"message": "встановити максимальний розмір для впроваджених ресурсів",
-		"description": "Options page label: 'set a maximum size for embedded resources'"
+		"message": "set maximum size",
+		"description": "Options page label: 'set maximum size'"
 	},
 	"optionMaxResourceSize": {
 		"message": "максимальний розмір (МБ)",
 		"description": "Options page label: 'maximum size (MB)'"
 	},
+	"optionSetMaxResourceDelay": {
+		"message": "set maximum download time",
+		"description": "Options page label: 'set maximum download time'"
+	},
+	"optionMaxResourceDelay": {
+		"message": "maximum download time (s)",
+		"description": "Options page label: 'maximum download time (s)'"
+	},
 	"optionPassReferrerOnError": {
 		"message": "pass \"Referer\" header after a cross-origin request error",
 		"description": "Options page label: 'pass \"Referer\" header after a cross-origin request error'"

+ 47 - 15
_locales/zh_CN/messages.json

@@ -303,23 +303,47 @@
 		"message": "移除用于备选设备屏幕的样式表",
 		"description": "Options page label: 'remove stylesheets for alternative devices to screens'"
 	},
-	"optionsOtherResourcesSubTitle": {
-		"message": "其他资源",
-		"description": "Options sub-title: 'Other resources'"
+	"optionsNetworkSubTitle": {
+		"message": "Network",
+		"description": "Options sub-title: 'Network'"
 	},
-	"optionRemoveScripts": {
-		"message": "移除脚本",
-		"description": "Options page label: 'remove scripts'"
+	"optionsBlockedResources": {
+		"message": "blocked resources",
+		"description": "Options page label: 'blocked resources'"
 	},
-	"optionRemoveVideo": {
-		"message": "移除视频资源",
-		"description": "Options page label: 'remove video sources'"
+	"optionsAcceptHeaders": {
+		"message": "\"Accept\" header",
+		"description": "Options page label: '\"Accept\" header'"
 	},
-	"optionRemoveAudio": {
-		"message": "移除音频资源",
-		"description": "Options page label: 'remove audio sources'"
+	"optionResourceScript": {
+		"message": "scripts",
+		"description": "Options page label: 'scripts'"
 	},
-	"optionsDestionationSubTitle": {
+	"optionResourceVideo": {
+		"message": "videos",
+		"description": "Options page label: 'videos'"
+	},
+	"optionResourceAudio": {
+		"message": "audios",
+		"description": "Options page label: 'audios'"
+	},
+	"optionResourceDocument": {
+		"message": "documents",
+		"description": "Options page label: 'documents'"
+	},
+	"optionResourceFont": {
+		"message": "fonts",
+		"description": "Options page label: 'fonts'"
+	},
+	"optionResourceStylesheet": {
+		"message": "stylesheets",
+		"description": "Options page label: 'stylesheets'"
+	},
+	"optionResourceImage": {
+		"message": "images",
+		"description": "Options page label: 'images'"
+	},
+	"optionsDestinationSubTitle": {
 		"message": "保存位置",
 		"description": "Options sub-title: 'Destination'"
 	},
@@ -484,13 +508,21 @@
 		"description": "Options page label: 'display stats in the console after processing'"
 	},
 	"optionSetMaxResourceSize": {
-		"message": "设置嵌入式资源的大小上限",
-		"description": "Options page label: 'set a maximum size for embedded resources'"
+		"message": "set maximum size",
+		"description": "Options page label: 'set maximum size'"
 	},
 	"optionMaxResourceSize": {
 		"message": "大小上限(兆字节)",
 		"description": "Options page label: 'maximum size (MB)'"
 	},
+	"optionSetMaxResourceDelay": {
+		"message": "set maximum download time",
+		"description": "Options page label: 'set maximum download time'"
+	},
+	"optionMaxResourceDelay": {
+		"message": "maximum download time (s)",
+		"description": "Options page label: 'maximum download time (s)'"
+	},
 	"optionPassReferrerOnError": {
 		"message": "跨域请求失败时添加来源地址头信息(Referer)",
 		"description": "Options page label: 'pass \"Referer\" header after a cross-origin request error'"

+ 47 - 15
_locales/zh_TW/messages.json

@@ -303,23 +303,47 @@
 		"message": "移除用於備選設備屏幕的樣式表",
 		"description": "Options page label: 'remove stylesheets for alternative devices to screens'"
 	},
-	"optionsOtherResourcesSubTitle": {
-		"message": "其他資源",
-		"description": "Options sub-title: 'Other resources'"
+	"optionsNetworkSubTitle": {
+		"message": "Network",
+		"description": "Options sub-title: 'Network'"
 	},
-	"optionRemoveScripts": {
-		"message": "移除腳本",
-		"description": "Options page label: 'remove scripts'"
+	"optionsBlockedResources": {
+		"message": "blocked resources",
+		"description": "Options page label: 'blocked resources'"
 	},
-	"optionRemoveVideo": {
-		"message": "移除視頻資源",
-		"description": "Options page label: 'remove video sources'"
+	"optionsAcceptHeaders": {
+		"message": "\"Accept\" header",
+		"description": "Options page label: '\"Accept\" header'"
 	},
-	"optionRemoveAudio": {
-		"message": "移除音頻資源",
-		"description": "Options page label: 'remove audio sources'"
+	"optionResourceScript": {
+		"message": "scripts",
+		"description": "Options page label: 'scripts'"
 	},
-	"optionsDestionationSubTitle": {
+	"optionResourceVideo": {
+		"message": "videos",
+		"description": "Options page label: 'videos'"
+	},
+	"optionResourceAudio": {
+		"message": "audios",
+		"description": "Options page label: 'audios'"
+	},
+	"optionResourceDocument": {
+		"message": "documents",
+		"description": "Options page label: 'documents'"
+	},
+	"optionResourceFont": {
+		"message": "fonts",
+		"description": "Options page label: 'fonts'"
+	},
+	"optionResourceStylesheet": {
+		"message": "stylesheets",
+		"description": "Options page label: 'stylesheets'"
+	},
+	"optionResourceImage": {
+		"message": "images",
+		"description": "Options page label: 'images'"
+	},
+	"optionsDestinationSubTitle": {
 		"message": "保存位置",
 		"description": "Options sub-title: 'Destination'"
 	},
@@ -484,13 +508,21 @@
 		"description": "Options page label: 'display stats in the console after processing'"
 	},
 	"optionSetMaxResourceSize": {
-		"message": "設置嵌入式資源的大小上限",
-		"description": "Options page label: 'set a maximum size for embedded resources'"
+		"message": "set maximum size",
+		"description": "Options page label: 'set maximum size'"
 	},
 	"optionMaxResourceSize": {
 		"message": "大小上限(兆字節)",
 		"description": "Options page label: 'maximum size (MB)'"
 	},
+	"optionSetMaxResourceDelay": {
+		"message": "set maximum download time",
+		"description": "Options page label: 'set maximum download time'"
+	},
+	"optionMaxResourceDelay": {
+		"message": "maximum download time (s)",
+		"description": "Options page label: 'maximum download time (s)'"
+	},
 	"optionPassReferrerOnError": {
 		"message": "跨域請求失敗時添加來源地址頭信息(Referer)",
 		"description": "Options page label: 'pass \"Referer\" header after a cross-origin request error'"

+ 7 - 4
cli/single-file-cli-api.js

@@ -33,7 +33,6 @@ const DEFAULT_OPTIONS = {
 	removeUnusedFonts: true,
 	removeFrames: false,
 	removeImports: true,
-	removeScripts: true,
 	compressHTML: true,
 	compressCSS: false,
 	loadDeferredImages: true,
@@ -50,8 +49,6 @@ const DEFAULT_OPTIONS = {
 	filenameReplacementCharacter: "_",
 	maxResourceSizeEnabled: false,
 	maxResourceSize: 10,
-	removeAudioSrc: true,
-	removeVideoSrc: true,
 	backgroundSave: true,
 	removeAlternativeFonts: true,
 	removeAlternativeMedias: true,
@@ -64,7 +61,13 @@ const DEFAULT_OPTIONS = {
 	includeBOM: false,
 	insertMetaCSP: true,
 	insertMetaNoIndex: false,
-	insertSingleFileComment: true
+	insertSingleFileComment: true,
+	blockImages: false,
+	blockStylesheets: false,
+	blockFont: false,
+	blockScripts: true,
+	blockVideos: true,
+	blockAudios: true
 };
 const STATE_PROCESSING = "processing";
 const STATE_PROCESSED = "processed";

+ 11 - 5
src/extension/core/bg/config.js

@@ -38,7 +38,6 @@ const DEFAULT_CONFIG = {
 	removeUnusedFonts: true,
 	removeFrames: false,
 	removeImports: true,
-	removeScripts: true,
 	compressHTML: true,
 	compressCSS: false,
 	loadDeferredImages: true,
@@ -65,8 +64,6 @@ const DEFAULT_CONFIG = {
 	progressBarEnabled: true,
 	maxResourceSizeEnabled: false,
 	maxResourceSize: 10,
-	removeAudioSrc: true,
-	removeVideoSrc: true,
 	displayInfobar: true,
 	displayStats: false,
 	backgroundSave: BACKGROUND_SAVE_DEFAULT,
@@ -125,7 +122,13 @@ const DEFAULT_CONFIG = {
 	},
 	moveStylesInHead: false,
 	networkTimeout: 0,
-	woleetKey: ""
+	woleetKey: "",
+	blockImages: false,
+	blockStylesheets: false,
+	blockFont: false,
+	blockScripts: true,
+	blockVideos: true,
+	blockAudios: true
 };
 
 const DEFAULT_RULES = [{
@@ -173,7 +176,7 @@ async function upgrade() {
 			config.rules = DEFAULT_RULES;
 		}
 		Object.keys(config.profiles).forEach(profileName => applyUpgrade(config.profiles[profileName]));
-		await configStorage.remove(["profiles", "defaultProfile", "rules"]);
+		await configStorage.remove(["profiles", "rules"]);
 		await configStorage.set({ profiles: config.profiles, rules: config.rules });
 	}
 	if (!config.maxParallelWorkers) {
@@ -182,6 +185,9 @@ async function upgrade() {
 }
 
 function applyUpgrade(config) {
+	upgradeOldConfig(config, "removeScripts", "blockScripts");
+	upgradeOldConfig(config, "removeVideoSrc", "blockVideos");
+	upgradeOldConfig(config, "removeAudioSrc", "blockAudios");
 	Object.keys(DEFAULT_CONFIG).forEach(configKey => upgradeConfig(config, configKey));
 }
 

+ 127 - 65
src/extension/ui/bg/ui-options.js

@@ -32,7 +32,19 @@ const removeUnusedStylesLabel = document.getElementById("removeUnusedStylesLabel
 const removeUnusedFontsLabel = document.getElementById("removeUnusedFontsLabel");
 const removeFramesLabel = document.getElementById("removeFramesLabel");
 const removeImportsLabel = document.getElementById("removeImportsLabel");
-const removeScriptsLabel = document.getElementById("removeScriptsLabel");
+const blockScriptsLabel = document.getElementById("blockScriptsLabel");
+const blockAudiosLabel = document.getElementById("blockAudiosLabel");
+const blockVideosLabel = document.getElementById("blockVideosLabel");
+const blockFontsLabel = document.getElementById("blockFontsLabel");
+const blockStylesheetsLabel = document.getElementById("blockStylesheetsLabel");
+const blockImagesLabel = document.getElementById("blockImagesLabel");
+const acceptHeaderDocumentLabel = document.getElementById("acceptHeaderDocumentLabel");
+const acceptHeaderScriptLabel = document.getElementById("acceptHeaderScriptLabel");
+const acceptHeaderAudioLabel = document.getElementById("acceptHeaderAudioLabel");
+const acceptHeaderVideoLabel = document.getElementById("acceptHeaderVideoLabel");
+const acceptHeaderFontLabel = document.getElementById("acceptHeaderFontLabel");
+const acceptHeaderStylesheetLabel = document.getElementById("acceptHeaderStylesheetLabel");
+const acceptHeaderImageLabel = document.getElementById("acceptHeaderImageLabel");
 const saveRawPageLabel = document.getElementById("saveRawPageLabel");
 const insertMetaCSPLabel = document.getElementById("insertMetaCSPLabel");
 const saveToClipboardLabel = document.getElementById("saveToClipboardLabel");
@@ -60,14 +72,14 @@ const filenameMaxLengthCharsUnitLabel = document.getElementById("filenameMaxLeng
 const shadowEnabledLabel = document.getElementById("shadowEnabledLabel");
 const setMaxResourceSizeLabel = document.getElementById("setMaxResourceSizeLabel");
 const maxResourceSizeLabel = document.getElementById("maxResourceSizeLabel");
+const setMaxResourceDelayLabel = document.getElementById("setMaxResourceDelayLabel");
+const maxResourceDelayLabel = document.getElementById("maxResourceDelayLabel");
 const confirmFilenameLabel = document.getElementById("confirmFilenameLabel");
 const filenameConflictActionLabel = document.getElementById("filenameConflictActionLabel");
 const filenameConflictActionUniquifyLabel = document.getElementById("filenameConflictActionUniquifyLabel");
 const filenameConflictActionOverwriteLabel = document.getElementById("filenameConflictActionOverwriteLabel");
 const filenameConflictActionPromptLabel = document.getElementById("filenameConflictActionPromptLabel");
 const filenameConflictActionSkipLabel = document.getElementById("filenameConflictActionSkipLabel");
-const removeAudioLabel = document.getElementById("removeAudioLabel");
-const removeVideoLabel = document.getElementById("removeVideoLabel");
 const displayInfobarLabel = document.getElementById("displayInfobarLabel");
 const displayStatsLabel = document.getElementById("displayStatsLabel");
 const backgroundSaveLabel = document.getElementById("backgroundSaveLabel");
@@ -95,7 +107,9 @@ const htmlContentLabel = document.getElementById("htmlContentLabel");
 const imagesLabel = document.getElementById("imagesLabel");
 const stylesheetsLabel = document.getElementById("stylesheetsLabel");
 const fontsLabel = document.getElementById("fontsLabel");
-const otherResourcesLabel = document.getElementById("otherResourcesLabel");
+const networkLabel = document.getElementById("networkLabel");
+const blockResourcesLabel = document.getElementById("blockResourcesLabel");
+const acceptHeadersLabel = document.getElementById("acceptHeadersLabel");
 const destinationLabel = document.getElementById("destinationLabel");
 const bookmarksLabel = document.getElementById("bookmarksLabel");
 const autoSaveLabel = document.getElementById("autoSaveLabel");
@@ -135,7 +149,19 @@ const removeUnusedStylesInput = document.getElementById("removeUnusedStylesInput
 const removeUnusedFontsInput = document.getElementById("removeUnusedFontsInput");
 const removeFramesInput = document.getElementById("removeFramesInput");
 const removeImportsInput = document.getElementById("removeImportsInput");
-const removeScriptsInput = document.getElementById("removeScriptsInput");
+const blockScriptsInput = document.getElementById("blockScriptsInput");
+const blockVideosInput = document.getElementById("blockVideosInput");
+const blockAudiosInput = document.getElementById("blockAudiosInput");
+const blockFontsInput = document.getElementById("blockFontsInput");
+const blockStylesheetsInput = document.getElementById("blockStylesheetsInput");
+const blockImagesInput = document.getElementById("blockImagesInput");
+const acceptHeaderDocumentInput = document.getElementById("acceptHeaderDocumentInput");
+const acceptHeaderScriptInput = document.getElementById("acceptHeaderScriptInput");
+const acceptHeaderAudioInput = document.getElementById("acceptHeaderAudioInput");
+const acceptHeaderVideoInput = document.getElementById("acceptHeaderVideoInput");
+const acceptHeaderFontInput = document.getElementById("acceptHeaderFontInput");
+const acceptHeaderStylesheetInput = document.getElementById("acceptHeaderStylesheetInput");
+const acceptHeaderImageInput = document.getElementById("acceptHeaderImageInput");
 const saveRawPageInput = document.getElementById("saveRawPageInput");
 const insertMetaCSPInput = document.getElementById("insertMetaCSPInput");
 const saveToClipboardInput = document.getElementById("saveToClipboardInput");
@@ -162,10 +188,10 @@ const filenameMaxLengthUnitInput = document.getElementById("filenameMaxLengthUni
 const shadowEnabledInput = document.getElementById("shadowEnabledInput");
 const maxResourceSizeInput = document.getElementById("maxResourceSizeInput");
 const maxResourceSizeEnabledInput = document.getElementById("maxResourceSizeEnabledInput");
+const maxResourceDelayInput = document.getElementById("maxResourceDelayInput");
+const maxResourceDelayEnabledInput = document.getElementById("maxResourceDelayEnabledInput");
 const confirmFilenameInput = document.getElementById("confirmFilenameInput");
 const filenameConflictActionInput = document.getElementById("filenameConflictActionInput");
-const removeAudioSrcInput = document.getElementById("removeAudioSrcInput");
-const removeVideoSrcInput = document.getElementById("removeVideoSrcInput");
 const displayInfobarInput = document.getElementById("displayInfobarInput");
 const displayStatsInput = document.getElementById("displayStatsInput");
 const backgroundSaveInput = document.getElementById("backgroundSaveInput");
@@ -410,12 +436,6 @@ expandAllButton.addEventListener("click", () => {
 	}
 	document.querySelectorAll("details").forEach(detailElement => detailElement.open = Boolean(expandAllButton.className));
 }, false);
-removeScriptsInput.addEventListener("click", () => {
-	if (!removeScriptsInput.checked) {
-		removeHiddenElementsInput.checked = false;
-		removeUnusedStylesInput.checked = false;
-	}
-}, false);
 saveCreatedBookmarksInput.addEventListener("click", saveCreatedBookmarks, false);
 passReferrerOnErrorInput.addEventListener("click", passReferrerOnError, false);
 autoSaveExternalSaveInput.addEventListener("click", () => enableExternalSave(autoSaveExternalSaveInput), false);
@@ -484,7 +504,19 @@ removeUnusedStylesLabel.textContent = browser.i18n.getMessage("optionRemoveUnuse
 removeUnusedFontsLabel.textContent = browser.i18n.getMessage("optionRemoveUnusedFonts");
 removeFramesLabel.textContent = browser.i18n.getMessage("optionRemoveFrames");
 removeImportsLabel.textContent = browser.i18n.getMessage("optionRemoveImports");
-removeScriptsLabel.textContent = browser.i18n.getMessage("optionRemoveScripts");
+blockScriptsLabel.textContent = browser.i18n.getMessage("optionResourceScript");
+blockAudiosLabel.textContent = browser.i18n.getMessage("optionResourceAudio");
+blockVideosLabel.textContent = browser.i18n.getMessage("optionResourceVideo");
+blockFontsLabel.textContent = browser.i18n.getMessage("optionResourceFont");
+blockStylesheetsLabel.textContent = browser.i18n.getMessage("optionResourceStylesheet");
+blockImagesLabel.textContent = browser.i18n.getMessage("optionResourceImage");
+acceptHeaderDocumentLabel.textContent = browser.i18n.getMessage("optionResourceDocument");
+acceptHeaderScriptLabel.textContent = browser.i18n.getMessage("optionResourceScript");
+acceptHeaderAudioLabel.textContent = browser.i18n.getMessage("optionResourceAudio");
+acceptHeaderVideoLabel.textContent = browser.i18n.getMessage("optionResourceVideo");
+acceptHeaderFontLabel.textContent = browser.i18n.getMessage("optionResourceFont");
+acceptHeaderStylesheetLabel.textContent = browser.i18n.getMessage("optionResourceStylesheet");
+acceptHeaderImageLabel.textContent = browser.i18n.getMessage("optionResourceImage");
 saveRawPageLabel.textContent = browser.i18n.getMessage("optionSaveRawPage");
 insertMetaCSPLabel.textContent = browser.i18n.getMessage("optionInsertMetaCSP");
 saveToClipboardLabel.textContent = browser.i18n.getMessage("optionSaveToClipboard");
@@ -512,14 +544,14 @@ filenameMaxLengthCharsUnitLabel.textContent = browser.i18n.getMessage("optionFil
 shadowEnabledLabel.textContent = browser.i18n.getMessage("optionDisplayShadow");
 setMaxResourceSizeLabel.textContent = browser.i18n.getMessage("optionSetMaxResourceSize");
 maxResourceSizeLabel.textContent = browser.i18n.getMessage("optionMaxResourceSize");
+setMaxResourceDelayLabel.textContent = browser.i18n.getMessage("optionSetMaxResourceDelay");
+maxResourceDelayLabel.textContent = browser.i18n.getMessage("optionMaxResourceDelay");
 confirmFilenameLabel.textContent = browser.i18n.getMessage("optionConfirmFilename");
 filenameConflictActionLabel.textContent = browser.i18n.getMessage("optionFilenameConflictAction");
 filenameConflictActionUniquifyLabel.textContent = browser.i18n.getMessage("optionFilenameConflictActionUniquify");
 filenameConflictActionOverwriteLabel.textContent = browser.i18n.getMessage("optionFilenameConflictActionOverwrite");
 filenameConflictActionPromptLabel.textContent = browser.i18n.getMessage("optionFilenameConflictActionPrompt");
 filenameConflictActionSkipLabel.textContent = browser.i18n.getMessage("optionFilenameConflictActionSkip");
-removeAudioLabel.textContent = browser.i18n.getMessage("optionRemoveAudio");
-removeVideoLabel.textContent = browser.i18n.getMessage("optionRemoveVideo");
 displayInfobarLabel.textContent = browser.i18n.getMessage("optionDisplayInfobar");
 displayStatsLabel.textContent = browser.i18n.getMessage("optionDisplayStats");
 backgroundSaveLabel.textContent = browser.i18n.getMessage("optionBackgroundSave");
@@ -548,8 +580,10 @@ htmlContentLabel.textContent = browser.i18n.getMessage("optionsHTMLContentSubTit
 imagesLabel.textContent = browser.i18n.getMessage("optionsImagesSubTitle");
 stylesheetsLabel.textContent = browser.i18n.getMessage("optionsStylesheetsSubTitle");
 fontsLabel.textContent = browser.i18n.getMessage("optionsFontsSubTitle");
-otherResourcesLabel.textContent = browser.i18n.getMessage("optionsOtherResourcesSubTitle");
-destinationLabel.textContent = browser.i18n.getMessage("optionsDestionationSubTitle");
+networkLabel.textContent = browser.i18n.getMessage("optionsNetworkSubTitle");
+blockResourcesLabel.textContent = browser.i18n.getMessage("optionsBlockedResources");
+acceptHeadersLabel.textContent = browser.i18n.getMessage("optionsAcceptHeaders");
+destinationLabel.textContent = browser.i18n.getMessage("optionsDestinationSubTitle");
 bookmarksLabel.textContent = browser.i18n.getMessage("optionsBookmarkSubTitle");
 autoSaveLabel.textContent = browser.i18n.getMessage("optionsAutoSaveSubTitle");
 miscLabel.textContent = browser.i18n.getMessage("optionsMiscSubTitle");
@@ -700,7 +734,19 @@ async function refresh(profileName) {
 	removeUnusedFontsInput.checked = profileOptions.removeUnusedFonts;
 	removeFramesInput.checked = profileOptions.removeFrames;
 	removeImportsInput.checked = profileOptions.removeImports;
-	removeScriptsInput.checked = profileOptions.removeScripts;
+	blockScriptsInput.checked = profileOptions.blockScripts;
+	blockVideosInput.checked = profileOptions.blockVideos;
+	blockAudiosInput.checked = profileOptions.blockAudios;
+	blockFontsInput.checked = profileOptions.blockFonts;
+	blockStylesheetsInput.checked = profileOptions.blockStylesheets;
+	blockImagesInput.checked = profileOptions.blockImages;
+	acceptHeaderDocumentInput.value = profileOptions.acceptHeaders.document;
+	acceptHeaderScriptInput.value = profileOptions.acceptHeaders.script;
+	acceptHeaderAudioInput.value = profileOptions.acceptHeaders.audio;
+	acceptHeaderVideoInput.value = profileOptions.acceptHeaders.video;
+	acceptHeaderFontInput.value = profileOptions.acceptHeaders.font;
+	acceptHeaderStylesheetInput.value = profileOptions.acceptHeaders.stylesheet;
+	acceptHeaderImageInput.value = profileOptions.acceptHeaders.image;
 	saveRawPageInput.checked = profileOptions.saveRawPage;
 	insertMetaCSPInput.checked = profileOptions.insertMetaCSP;
 	saveToClipboardInput.checked = profileOptions.saveToClipboard;
@@ -733,12 +779,13 @@ async function refresh(profileName) {
 	filenameMaxLengthUnitInput.value = profileOptions.filenameMaxLengthUnit;
 	shadowEnabledInput.checked = profileOptions.shadowEnabled;
 	maxResourceSizeEnabledInput.checked = profileOptions.maxResourceSizeEnabled;
-	maxResourceSizeInput.value = profileOptions.maxResourceSize;
+	maxResourceSizeInput.value = profileOptions.maxResourceSizeEnabled ? profileOptions.maxResourceSize : 10;
 	maxResourceSizeInput.disabled = !profileOptions.maxResourceSizeEnabled;
+	maxResourceDelayEnabledInput.checked = Boolean(profileOptions.networkTimeout);
+	maxResourceDelayInput.value = profileOptions.networkTimeout ? profileOptions.networkTimeout / 1000 : 60;
+	maxResourceDelayInput.disabled = !profileOptions.networkTimeout;
 	confirmFilenameInput.checked = profileOptions.confirmFilename;
 	filenameConflictActionInput.value = profileOptions.filenameConflictAction;
-	removeAudioSrcInput.checked = profileOptions.removeAudioSrc;
-	removeVideoSrcInput.checked = profileOptions.removeVideoSrc;
 	displayInfobarInput.checked = profileOptions.displayInfobar;
 	displayStatsInput.checked = profileOptions.displayStats;
 	backgroundSaveInput.checked = profileOptions.backgroundSave;
@@ -800,7 +847,21 @@ async function update() {
 			removeUnusedFonts: removeUnusedFontsInput.checked,
 			removeFrames: removeFramesInput.checked,
 			removeImports: removeImportsInput.checked,
-			removeScripts: removeScriptsInput.checked,
+			blockScripts: blockScriptsInput.checked,
+			blockVideos: blockVideosInput.checked,
+			blockAudios: blockAudiosInput.checked,
+			blockFonts: blockFontsInput.checked,
+			blockStylesheets: blockStylesheetsInput.checked,
+			blockImages: blockImagesInput.checked,
+			acceptHeaders: {
+				document: acceptHeaderDocumentInput.value,
+				script: acceptHeaderScriptInput.value,
+				audio: acceptHeaderAudioInput.value,
+				video: acceptHeaderVideoInput.value,
+				font: acceptHeaderFontInput.value,
+				stylesheet: acceptHeaderStylesheetInput.value,
+				image: acceptHeaderImageInput.value
+			},
 			saveRawPage: saveRawPageInput.checked,
 			insertMetaCSP: insertMetaCSPInput.checked,
 			saveToClipboard: saveToClipboardInput.checked,
@@ -825,11 +886,10 @@ async function update() {
 			filenameMaxLengthUnit: filenameMaxLengthUnitInput.value,
 			shadowEnabled: shadowEnabledInput.checked,
 			maxResourceSizeEnabled: maxResourceSizeEnabledInput.checked,
-			maxResourceSize: Math.max(maxResourceSizeInput.value, 0),
+			maxResourceSize: maxResourceSizeEnabledInput.checked ? Math.max(maxResourceSizeInput.value, 0) : 10,
+			networkTimeout: maxResourceDelayEnabledInput.checked ? Math.max(maxResourceDelayInput.value * 1000, 60) : 0,
 			confirmFilename: confirmFilenameInput.checked,
 			filenameConflictAction: filenameConflictActionInput.value,
-			removeAudioSrc: removeAudioSrcInput.checked,
-			removeVideoSrc: removeVideoSrcInput.checked,
 			displayInfobar: displayInfobarInput.checked,
 			displayStats: displayStatsInput.checked,
 			backgroundSave: backgroundSaveInput.checked,
@@ -1050,47 +1110,49 @@ async function getHelpContents() {
 	const items = doc.querySelectorAll("[data-options-label]");
 	items.forEach(itemElement => {
 		const optionLabel = document.getElementById(itemElement.dataset.optionsLabel);
-		const helpIconWrapper = document.createElement("span");
-		const helpIconContainer = document.createElement("span");
-		const helpIcon = document.createElement("img");
-		helpIcon.src = HELP_ICON_URL;
-		helpIconWrapper.className = "help-icon-wrapper";
-		const labelWords = optionLabel.textContent.split(/\s+/);
-		if (labelWords.length > 1) {
-			helpIconWrapper.textContent = labelWords.pop();
-			optionLabel.textContent = labelWords.join(" ") + " ";
-		}
-		helpIconContainer.className = "help-icon";
-		helpIconContainer.onclick = () => {
-			helpContent.hidden = !helpContent.hidden;
-			return false;
-		};
-		helpIcon.tabIndex = 0;
-		helpIconContainer.onkeyup = event => {
-			if (event.code == "Enter") {
+		if (optionLabel) {
+			const helpIconWrapper = document.createElement("span");
+			const helpIconContainer = document.createElement("span");
+			const helpIcon = document.createElement("img");
+			helpIcon.src = HELP_ICON_URL;
+			helpIconWrapper.className = "help-icon-wrapper";
+			const labelWords = optionLabel.textContent.split(/\s+/);
+			if (labelWords.length > 1) {
+				helpIconWrapper.textContent = labelWords.pop();
+				optionLabel.textContent = labelWords.join(" ") + " ";
+			}
+			helpIconContainer.className = "help-icon";
+			helpIconContainer.onclick = () => {
 				helpContent.hidden = !helpContent.hidden;
 				return false;
-			}
-		};
-		helpIconContainer.appendChild(helpIcon);
-		helpIconWrapper.appendChild(helpIconContainer);
-		optionLabel.appendChild(helpIconWrapper);
-		const helpContent = document.createElement("div");
-		helpContent.hidden = true;
-		helpContent.className = "help-content";
-		itemElement.childNodes.forEach(node => {
-			if (node instanceof HTMLElement && node.className != "option") {
-				helpContent.appendChild(document.importNode(node, true));
-			}
-		});
-		helpContent.querySelectorAll("a[href]").forEach(linkElement => {
-			const hrefValue = linkElement.getAttribute("href");
-			if (hrefValue.startsWith("#")) {
-				linkElement.href = browser.runtime.getURL(HELP_PAGE_PATH + linkElement.getAttribute("href"));
-				linkElement.target = "_blank";
-			}
-		});
-		optionLabel.parentElement.insertAdjacentElement("afterEnd", helpContent);
+			};
+			helpIcon.tabIndex = 0;
+			helpIconContainer.onkeyup = event => {
+				if (event.code == "Enter") {
+					helpContent.hidden = !helpContent.hidden;
+					return false;
+				}
+			};
+			helpIconContainer.appendChild(helpIcon);
+			helpIconWrapper.appendChild(helpIconContainer);
+			optionLabel.appendChild(helpIconWrapper);
+			const helpContent = document.createElement("div");
+			helpContent.hidden = true;
+			helpContent.className = "help-content";
+			itemElement.childNodes.forEach(node => {
+				if (node instanceof HTMLElement && node.className != "option") {
+					helpContent.appendChild(document.importNode(node, true));
+				}
+			});
+			helpContent.querySelectorAll("a[href]").forEach(linkElement => {
+				const hrefValue = linkElement.getAttribute("href");
+				if (hrefValue.startsWith("#")) {
+					linkElement.href = browser.runtime.getURL(HELP_PAGE_PATH + linkElement.getAttribute("href"));
+					linkElement.target = "_blank";
+				}
+			});
+			optionLabel.parentElement.insertAdjacentElement("afterEnd", helpContent);
+		}
 	});
 }
 

+ 52 - 43
src/extension/ui/pages/help.html

@@ -171,7 +171,7 @@
 						</ul>
 					</li>
 					<li data-options-label="filenameMaxLengthLabel"> <span class="option">Option: max length</span>
-						<p>Specify the maximum length of the filename (without the extension) and its unit (bytes or
+						<p>Enter the maximum length of the filename (without the extension) and its unit (bytes or
 							characters).</p>
 					</li>
 					<li data-options-label="confirmFilenameLabel"> <span class="option">Option: open the "Save as"
@@ -219,13 +219,6 @@
 							considerably reduce the size of the file without altering the document most of the time. It
 							may also decrease the time needed to save a page.</p>
 					</li>
-					<li data-options-label="blockMixedContentLabel"> <span class="option"></span>Option: block mixed
-						contents</span>
-						<p>Check this option to block active content served from HTTP when viewing a page in HTTPS. You
-							can find more information about mixed content pages <a
-								href="https://developer.mozilla.org/docs/Web/Security/Mixed_content"
-								target="_blank">here</a>.</p>
-					</li>
 					<li data-options-label="saveOriginalURLsLabel"> <span class="option"></span>Option: save original
 						URLs of embedded resources</span>
 						<p>Check this option to save the URLs of the resources embedded into the saved page. URLs
@@ -296,7 +289,7 @@
 					</li>
 					<li data-options-label="loadDeferredImagesMaxIdleTimeLabel"> <span class="option">Option: maximum
 							idle time (ms)</span>
-						<p>Specify the maximum delay of time to wait for deferred images. You can increase this value if
+						<p>Enter the maximum delay of time to wait for deferred images. You can increase this value if
 							for example the network or system conditions are degraded. You can also decrease this value
 							otherwise.</p>
 					</li>
@@ -328,22 +321,6 @@
 						<p class="notice">It is recommended to <u>check</u> this option</p>
 					</li>
 				</ul>
-				<p>Other resources</p>
-				<ul>
-					<li data-options-label="removeScriptsLabel"> <span class="option">Option: remove scripts</span>
-						<p>Check this option to remove all the JavaScript scripts. Unchecking this option may alter the
-							document and may introduce privacy or security issues.</p>
-						<p class="notice">It is recommended to <u>check</u> this option</p>
-					</li>
-					<li data-options-label="removeVideoLabel"> <span class="option">Option: remove video sources</span>
-						<p>Check this option to empty the "src" attribute of all video elements.</p>
-						<p class="notice">It is recommended to <u>check</u> this option</p>
-					</li>
-					<li data-options-label="removeAudioLabel"> <span class="option">Option: remove audio sources</span>
-						<p>Check this option to empty the "src" attribute of all audio elements.</p>
-						<p class="notice">It is recommended to <u>check</u> this option</p>
-					</li>
-				</ul>
 				<p>Destination</p>
 				<ul>
 					<li data-options-label="saveToFilesystemLabel"> <span class="option">Option: save to
@@ -407,6 +384,54 @@
 						<p class="notice">It is recommended to <u>uncheck</u> this option</p>
 					</li>
 				</ul>
+				<p>Network</p>
+				<ul>
+					<li data-options-label="blockResourcesLabel"> <span class="option">Option: blocked resources</span>
+						<p>
+							Check the options below to block the download of the corresponding resource types.
+						</p>
+					</li>
+					<li data-options-label="blockMixedContentLabel"> <span class="option"></span>Option: block mixed
+						contents</span>
+						<p>Check this option to block active content served from HTTP when viewing a page in HTTPS. You
+							can find more information about mixed content pages <a
+								href="https://developer.mozilla.org/docs/Web/Security/Mixed_content"
+								target="_blank">here</a>.</p>
+					</li>
+					<li data-options-label="acceptHeadersLabel"> <span class="option">Option: "Accept" headers</span>
+						<p>
+							Enter the <a href="https://developer.mozilla.org/docs/Web/HTTP/Headers/Accept"
+								target="_blank">"Accept" HTTP
+								header</a> values below to adjust support for the corresponding resource types.
+						</p>
+						<p class="notice">It is recommended to leave these values <u>as they are</u></p>
+					</li>
+					<li data-options-label="setMaxResourceSizeLabel"> <span class="option">Option: set maximum size
+							(MB)</span>
+						<p>Check this option to remove from the saved page embedded resources (i.e. images, stylesheets,
+							scripts and iframes) that exceeds a given size.</p>
+					</li>
+					<li data-options-label="maxResourceSizeLabel"> <span class="option">Option: maximum size (MB)</span>
+						<p>Enter the maximum size in megabytes.</p>
+					</li>
+					<li data-options-label="setMaxResourceDelayLabel"> <span class="option">Option: set maximum
+							download delay (s)</span>
+						<p>Check this option to remove from the saved page embedded resources (i.e. images, stylesheets,
+							scripts and iframes) that exceeds a given download time.</p>
+					</li>
+					<li data-options-label="maxResourceDelayLabel"> <span class="option">Option: maximum download delay
+							(s)</span>
+						<p>Enter the maximum download delay in seconds.</p>
+					</li>
+					<li data-options-label="passReferrerOnErrorLabel"> <span class="option">Option: pass \"Referer\"
+							header on
+							cross-origin errors</span>
+						<p>Check this option to pass the HTTP header "Referer" with the "origin" policy after an 401,
+							403, or 404 HTTP error when downloading a cross-origin resource. You should enable this
+							option if you cannot download resources blocked by a hotlink protection.</p>
+						<p class="notice">It is recommended to <u>uncheck</u> this option</p>
+					</li>
+				</ul>
 				<p>Annotation editor</p>
 				<ul>
 					<li data-options-label="defaultEditorModeLabel"> <span class="option">Option: default mode</span>
@@ -509,7 +534,7 @@
 					</li>
 					<li data-options-label="autoSaveDelayLabel"> <span class="option">Option: auto-save waiting delay
 							after load (s)</span>
-						<p>Specify the delay in seconds to wait before saving a page when the "auto-save on page load or
+						<p>Enter the delay in seconds to wait before saving a page when the "auto-save on page load or
 							on page unload" or "auto-save on page load" is checked. </p>
 					</li>
 					<li data-options-label="autoSaveRepeatLabel"> <span class="option">Option: auto-save
@@ -517,7 +542,7 @@
 						<p>Check this option to auto-save pages periodically after load.</p>
 					</li>
 					<li data-options-label="autoSaveRepeatDelayLabel"> <span class="option">Option: period (s)</span>
-						<p>Specify the delay in seconds to wait before each page saving when the "auto-save
+						<p>Enter the delay in seconds to wait before each page saving when the "auto-save
 							periodically" option is checked. </p>
 					</li>
 					<li data-options-label="autoSaveExternalSaveLabel"> <span class="option">Option: save the page with
@@ -532,22 +557,6 @@
 				</ul>
 				<p>Misc.</p>
 				<ul>
-					<li data-options-label="setMaxResourceSizeLabel"> <span class="option">Option: set a maximum size
-							for embedded resources (MB)</span>
-						<p>Check this option to remove from the saved page embedded resources (i.e. images, stylesheets,
-							scripts and iframes) that exceeds a given size.</p>
-					</li>
-					<li data-options-label="maxResourceSizeLabel"> <span class="option">Option: maximum size (MB)</span>
-						<p>Specify the maximum size of embedded resources in megabytes.</p>
-					</li>
-					<li data-options-label="passReferrerOnErrorLabel"> <span class="option">Option: pass \"Referer\"
-							header on
-							cross-origin errors</span>
-						<p>Check this option to pass the HTTP header "Referer" with the "origin" policy after an 401,
-							403, or 404 HTTP error when downloading a cross-origin resource. You should enable this
-							option if you cannot download resources blocked by a hotlink protection.</p>
-						<p class="notice">It is recommended to <u>uncheck</u> this option</p>
-					</li>
 					<li data-options-label="addProofLabel"> <span class="option">Option: add proof od existence</span>
 						<p>Check this option to create a worldwide proof of the existence of the page you want to save.
 						</p>

+ 85 - 31
src/extension/ui/pages/options.html

@@ -108,10 +108,6 @@
 				<label for="removeFramesInput" id="removeFramesLabel"></label>
 				<input type="checkbox" id="removeFramesInput">
 			</div>
-			<div class="option">
-				<label for="blockMixedContentInput" id="blockMixedContentLabel"></label>
-				<input type="checkbox" id="blockMixedContentInput">
-			</div>
 			<div class="option">
 				<label for="saveOriginalURLsInput" id="saveOriginalURLsLabel"></label>
 				<input type="checkbox" id="saveOriginalURLsInput">
@@ -178,21 +174,6 @@
 				<input type="checkbox" id="removeAlternativeFontsInput">
 			</div>
 		</details>
-		<details>
-			<summary id="otherResourcesLabel"></summary>
-			<div class="option">
-				<label for="removeScriptsInput" id="removeScriptsLabel"></label>
-				<input type="checkbox" id="removeScriptsInput">
-			</div>
-			<div class="option">
-				<label for="removeVideoSrcInput" id="removeVideoLabel"></label>
-				<input type="checkbox" id="removeVideoSrcInput">
-			</div>
-			<div class="option">
-				<label for="removeAudioSrcInput" id="removeAudioLabel"></label>
-				<input type="checkbox" id="removeAudioSrcInput">
-			</div>
-		</details>
 		<details>
 			<summary id="destinationLabel"></summary>
 			<div class="option">
@@ -232,6 +213,91 @@
 				<input type="radio" id="saveWithCompanionInput" name="destinationInput">
 			</div>
 		</details>
+		<details>
+			<summary id="networkLabel"></summary>
+			<div class="option">
+				<label id="blockResourcesLabel"></label>
+			</div>
+			<div class="option second-level">
+				<label for="blockImagesInput" id="blockImagesLabel"></label>
+				<input type="checkbox" id="blockImagesInput">
+			</div>
+			<div class="option second-level">
+				<label for="blockStylesheetsInput" id="blockStylesheetsLabel"></label>
+				<input type="checkbox" id="blockStylesheetsInput">
+			</div>
+			<div class="option second-level">
+				<label for="blockFontsInput" id="blockFontsLabel"></label>
+				<input type="checkbox" id="blockFontsInput">
+			</div>
+			<div class="option second-level">
+				<label for="blockScriptsInput" id="blockScriptsLabel"></label>
+				<input type="checkbox" id="blockScriptsInput">
+			</div>
+			<div class="option second-level">
+				<label for="blockVideosInput" id="blockVideosLabel"></label>
+				<input type="checkbox" id="blockVideosInput">
+			</div>
+			<div class="option second-level">
+				<label for="blockAudiosInput" id="blockAudiosLabel"></label>
+				<input type="checkbox" id="blockAudiosInput">
+			</div>
+			<div class="option">
+				<label for="blockMixedContentInput" id="blockMixedContentLabel"></label>
+				<input type="checkbox" id="blockMixedContentInput">
+			</div>
+			<div class="option">
+				<label id="acceptHeadersLabel"></label>
+			</div>
+			<div class="option second-level">
+				<label for="acceptHeaderDocumentInput" id="acceptHeaderDocumentLabel"></label>
+				<input type="text" id="acceptHeaderDocumentInput">
+			</div>
+			<div class="option second-level">
+				<label for="acceptHeaderImageInput" id="acceptHeaderImageLabel"></label>
+				<input type="text" id="acceptHeaderImageInput">
+			</div>
+			<div class="option second-level">
+				<label for="acceptHeaderStylesheetInput" id="acceptHeaderStylesheetLabel"></label>
+				<input type="text" id="acceptHeaderStylesheetInput">
+			</div>
+			<div class="option second-level">
+				<label for="acceptHeaderFontInput" id="acceptHeaderFontLabel"></label>
+				<input type="text" id="acceptHeaderFontInput">
+			</div>
+			<div class="option second-level">
+				<label for="acceptHeaderScriptInput" id="acceptHeaderScriptLabel"></label>
+				<input type="text" id="acceptHeaderScriptInput">
+			</div>
+			<div class="option second-level">
+				<label for="acceptHeaderVideoInput" id="acceptHeaderVideoLabel"></label>
+				<input type="text" id="acceptHeaderVideoInput">
+			</div>
+			<div class="option second-level">
+				<label for="acceptHeaderAudioInput" id="acceptHeaderAudioLabel"></label>
+				<input type="text" id="acceptHeaderAudioInput">
+			</div>
+			<div class="option">
+				<label for="maxResourceSizeEnabledInput" id="setMaxResourceSizeLabel"></label>
+				<input type="checkbox" id="maxResourceSizeEnabledInput">
+			</div>
+			<div class="option second-level">
+				<label for="maxResourceSizeInput" id="maxResourceSizeLabel"></label>
+				<input type="number" id="maxResourceSizeInput" min="1">
+			</div>
+			<div class="option">
+				<label for="maxResourceDelayEnabledInput" id="setMaxResourceDelayLabel"></label>
+				<input type="checkbox" id="maxResourceDelayEnabledInput">
+			</div>
+			<div class="option second-level">
+				<label for="maxResourceDelayInput" id="maxResourceDelayLabel"></label>
+				<input type="number" id="maxResourceDelayInput" min="1">
+			</div>
+			<div class="option">
+				<label for="passReferrerOnErrorInput" id="passReferrerOnErrorLabel"></label>
+				<input type="checkbox" id="passReferrerOnErrorInput">
+			</div>
+		</details>
 		<details>
 			<summary id="editorLabel"></summary>
 			<div class="option">
@@ -321,18 +387,6 @@
 		</details>
 		<details>
 			<summary id="miscLabel"></summary>
-			<div class="option">
-				<label for="maxResourceSizeEnabledInput" id="setMaxResourceSizeLabel"></label>
-				<input type="checkbox" id="maxResourceSizeEnabledInput">
-			</div>
-			<div class="option second-level">
-				<label for="maxResourceSizeInput" id="maxResourceSizeLabel"></label>
-				<input type="number" id="maxResourceSizeInput" min="1">
-			</div>
-			<div class="option">
-				<label for="passReferrerOnErrorInput" id="passReferrerOnErrorLabel"></label>
-				<input type="checkbox" id="passReferrerOnErrorInput">
-			</div>
 			<div class="option">
 				<label for="addProofInput" id="addProofLabel"></label>
 				<input type="checkbox" id="addProofInput">

+ 6 - 6
src/single-file/modules/html-images-alt-minifier.js

@@ -23,7 +23,7 @@
 
 import * as srcsetParser from "./../vendor/html-srcset-parser.js";
 
-const EMPTY_IMAGE = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
+const EMPTY_RESOURCE = "data:,";
 
 export {
 	process
@@ -49,11 +49,11 @@ function process(doc) {
 
 function getImgSrcData(imgElement) {
 	let src = imgElement.getAttribute("src");
-	if (src == EMPTY_IMAGE) {
+	if (src == EMPTY_RESOURCE) {
 		src = null;
 	}
 	let srcset = getSourceSrc(imgElement.getAttribute("srcset"));
-	if (srcset == EMPTY_IMAGE) {
+	if (srcset == EMPTY_RESOURCE) {
 		srcset = null;
 	}
 	return { src, srcset };
@@ -66,14 +66,14 @@ function getSourceSrcData(sources) {
 	if (!src) {
 		source = sources.find(source => getSourceSrc(source.src));
 		src = source && source.src;
-		if (src == EMPTY_IMAGE) {
+		if (src == EMPTY_RESOURCE) {
 			src = null;
 		}
 	}
 	if (!srcset) {
 		source = sources.find(source => getSourceSrc(source.srcset));
 		srcset = source && source.srcset;
-		if (srcset == EMPTY_IMAGE) {
+		if (srcset == EMPTY_RESOURCE) {
 			srcset = null;
 		}
 	}
@@ -86,7 +86,7 @@ function setSrc(srcData, imgElement, pictureElement) {
 		imgElement.setAttribute("srcset", "");
 		imgElement.setAttribute("sizes", "");
 	} else {
-		imgElement.setAttribute("src", EMPTY_IMAGE);
+		imgElement.setAttribute("src", EMPTY_RESOURCE);
 		if (srcData.srcset) {
 			imgElement.setAttribute("srcset", srcData.srcset);
 		} else {

+ 194 - 235
src/single-file/single-file-core.js

@@ -102,13 +102,11 @@ const STAGES = [{
 		{ action: "insertShadowRootContents" },
 		{ action: "setInputValues" },
 		{ option: "moveStylesInHead", action: "moveStylesInHead" },
-		{ option: "removeScripts", action: "removeScripts" },
+		{ option: "blockScripts", action: "removeEmbedScripts" },
 		{ option: "selected", action: "removeUnselectedElements" },
-		{ option: "removeVideoSrc", action: "insertVideoPosters" },
-		{ option: "removeVideoSrc", action: "insertVideoLinks" },
+		{ option: "blockVideos", action: "insertVideoPosters" },
+		{ option: "blockVideos", action: "insertVideoLinks" },
 		{ option: "removeFrames", action: "removeFrames" },
-		{ option: "removeVideoSrc", action: "removeVideoSources" },
-		{ option: "removeAudioSrc", action: "removeAudioSources" },
 		{ action: "removeDiscardedResources" },
 		{ option: "removeHiddenElements", action: "removeHiddenElements" },
 		{ action: "resolveHrefs" },
@@ -129,7 +127,7 @@ const STAGES = [{
 		{ action: "processStylesheets" },
 		{ action: "processStyleAttributes" },
 		{ action: "processPageResources" },
-		{ option: "!removeScripts", action: "processScripts" }
+		{ action: "processScripts" }
 	]
 }, {
 	sequential: [
@@ -399,7 +397,6 @@ class BatchRequest {
 // ---------
 const PREFIXES_FORBIDDEN_DATA_URI = ["data:text/"];
 const PREFIX_DATA_URI_IMAGE_SVG = "data:image/svg+xml";
-const EMPTY_IMAGE = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
 const SCRIPT_TAG_FOUND = /<script/gi;
 const NOSCRIPT_TAG_FOUND = /<noscript/gi;
 const CANVAS_TAG_FOUND = /<canvas/gi;
@@ -525,6 +522,9 @@ class Processor {
 				this.doc.head.appendChild(metaElement);
 			}
 		}
+		const styleElement = this.doc.createElement("style");
+		styleElement.textContent = "img[src=\"data:,\"],source[src=\"data:,\"]{display:none!important}";
+		this.doc.head.appendChild(styleElement);
 		let size;
 		if (this.options.displayStats) {
 			size = util.getContentSize(this.doc.documentElement.outerHTML);
@@ -583,14 +583,14 @@ class Processor {
 							(imageData.size && !imageData.size.pxWidth && !imageData.size.pxHeight) ||
 							(imgElement.getAttribute(util.HIDDEN_CONTENT_ATTRIBUTE_NAME) == "")
 						)) {
-							imgElement.setAttribute("src", EMPTY_IMAGE);
+							imgElement.setAttribute("src", util.EMPTY_RESOURCE);
 						} else {
 							if (imageData.currentSrc) {
 								imgElement.dataset.singleFileOriginURL = imgElement.getAttribute("src");
 								imgElement.setAttribute("src", imageData.currentSrc);
 							}
 							if (this.options.loadDeferredImages) {
-								if ((!imgElement.getAttribute("src") || imgElement.getAttribute("src") == EMPTY_IMAGE) && imgElement.getAttribute("data-src")) {
+								if ((!imgElement.getAttribute("src") || imgElement.getAttribute("src") == util.EMPTY_RESOURCE) && imgElement.getAttribute("data-src")) {
 									imageData.src = imgElement.dataset.src;
 									imgElement.setAttribute("src", imgElement.dataset.src);
 									imgElement.removeAttribute("data-src");
@@ -738,15 +738,16 @@ class Processor {
 		this.doc.querySelectorAll("iframe, frame, object[type=\"text/html\"][data]").forEach(element => element.remove());
 	}
 
-	removeScripts() {
+	removeEmbedScripts() {
+		const JAVASCRIPT_URI_PREFIX = "javascript:";
 		this.onEventAttributeNames.forEach(attributeName => this.doc.querySelectorAll("[" + attributeName + "]").forEach(element => element.removeAttribute(attributeName)));
 		this.doc.querySelectorAll("[href]").forEach(element => {
-			if (element.href && element.href.match && element.href.match(/^\s*javascript:/)) {
-				element.setAttribute("href", "");
+			if (element.href && element.href.match && element.href.trim().startsWith(JAVASCRIPT_URI_PREFIX)) {
+				element.removeAttribute("href");
 			}
 		});
 		this.doc.querySelectorAll("[src]").forEach(element => {
-			if (element.src && element.src.match(/^\s*javascript:/)) {
+			if (element.src && element.src.trim().startsWith(JAVASCRIPT_URI_PREFIX)) {
 				element.removeAttribute("src");
 			}
 		});
@@ -756,32 +757,6 @@ class Processor {
 		scriptElements.forEach(element => element.remove());
 	}
 
-	removeVideoSources() {
-		const videoSourceElements = this.doc.querySelectorAll("video[src], video > source");
-		this.stats.set("discarded", "video sources", videoSourceElements.length);
-		this.stats.set("processed", "video sources", videoSourceElements.length);
-		videoSourceElements.forEach(element => {
-			if (element.tagName == "SOURCE") {
-				element.remove();
-			} else {
-				videoSourceElements.forEach(element => element.removeAttribute("src"));
-			}
-		});
-	}
-
-	removeAudioSources() {
-		const audioSourceElements = this.doc.querySelectorAll("audio[src], audio > source[src]");
-		this.stats.set("discarded", "audio sources", audioSourceElements.length);
-		this.stats.set("processed", "audio sources", audioSourceElements.length);
-		audioSourceElements.forEach(element => {
-			if (element.tagName == "SOURCE") {
-				element.remove();
-			} else {
-				audioSourceElements.forEach(element => element.removeAttribute("src"));
-			}
-		});
-	}
-
 	removeDiscardedResources() {
 		this.doc.querySelectorAll("." + util.SINGLE_FILE_UI_ELEMENT_CLASS).forEach(element => element.remove());
 		const noscriptPlaceholders = new Map();
@@ -803,13 +778,7 @@ class Processor {
 		objectElements.forEach(element => element.remove());
 		const replacedAttributeValue = this.doc.querySelectorAll("link[rel~=preconnect], link[rel~=prerender], link[rel~=dns-prefetch], link[rel~=preload], link[rel~=manifest], link[rel~=prefetch]");
 		replacedAttributeValue.forEach(element => {
-			let regExp;
-			if (this.options.removeScripts) {
-				regExp = /(preconnect|prerender|dns-prefetch|preload|prefetch|manifest)/g;
-			} else {
-				regExp = /(preconnect|prerender|dns-prefetch|prefetch|manifest)/g;
-			}
-			const relValue = element.getAttribute("rel").replace(regExp, "").trim();
+			const relValue = element.getAttribute("rel").replace(/(preconnect|prerender|dns-prefetch|preload|prefetch|manifest)/g, "").trim();
 			if (relValue.length) {
 				element.setAttribute("rel", relValue);
 			} else {
@@ -1004,21 +973,7 @@ class Processor {
 
 	async resolveStylesheetURLs() {
 		await Promise.all(Array.from(this.doc.querySelectorAll("style, link[rel*=stylesheet]")).map(async element => {
-			const options = {
-				maxResourceSize: this.options.maxResourceSize,
-				maxResourceSizeEnabled: this.options.maxResourceSizeEnabled,
-				url: this.options.url,
-				charset: this.charset,
-				compressCSS: this.options.compressCSS,
-				updatedResources: this.options.updatedResources,
-				rootDocument: this.options.rootDocument,
-				frameId: this.options.windowId,
-				resourceReferrer: this.options.resourceReferrer,
-				blockMixedContent: this.options.blockMixedContent,
-				saveOriginalURLs: this.options.saveOriginalURLs,
-				acceptHeaders: this.options.acceptHeaders,
-				networkTimeout: this.options.networkTimeout
-			};
+			const options = Object.assign({}, this.options, { charset: this.charset });
 			let mediaText;
 			if (element.media) {
 				mediaText = element.media.toLowerCase();
@@ -1045,17 +1000,19 @@ class Processor {
 		}
 
 		async function processElement(element, stylesheetInfo, stylesheets, baseURI, options, workStyleElement) {
-			stylesheets.set(element, stylesheetInfo);
-			let stylesheetContent = await getStylesheetContent(element, baseURI, options, workStyleElement);
-			if (!matchCharsetEquals(stylesheetContent, options.charset)) {
-				options = Object.assign({}, options, { charset: getCharset(stylesheetContent) });
-				stylesheetContent = await getStylesheetContent(element, baseURI, options, workStyleElement);
-			}
 			let stylesheet;
-			try {
-				stylesheet = cssTree.parse(removeCssComments(stylesheetContent));
-			} catch (error) {
-				// ignored
+			stylesheets.set(element, stylesheetInfo);
+			if (!options.blockStylesheets) {
+				let stylesheetContent = await getStylesheetContent(element, baseURI, options, workStyleElement);
+				if (!matchCharsetEquals(stylesheetContent, options.charset)) {
+					options = Object.assign({}, options, { charset: getCharset(stylesheetContent) });
+					stylesheetContent = await getStylesheetContent(element, baseURI, options, workStyleElement);
+				}
+				try {
+					stylesheet = cssTree.parse(removeCssComments(stylesheetContent));
+				} catch (error) {
+					// ignored
+				}
 			}
 			if (stylesheet && stylesheet.children) {
 				if (options.compressCSS) {
@@ -1069,10 +1026,12 @@ class Processor {
 
 		async function getStylesheetContent(element, baseURI, options, workStyleElement) {
 			let content;
-			if (element.tagName == "LINK") {
-				content = await ProcessorHelper.resolveLinkStylesheetURLs(element.href, baseURI, options, workStyleElement);
-			} else {
-				content = await ProcessorHelper.resolveImportURLs(element.textContent, baseURI, options, workStyleElement);
+			if (!options.blockStylesheets) {
+				if (element.tagName == "LINK") {
+					content = await ProcessorHelper.resolveLinkStylesheetURLs(element.href, baseURI, options, workStyleElement);
+				} else {
+					content = await ProcessorHelper.resolveImportURLs(element.textContent, baseURI, options, workStyleElement);
+				}
 			}
 			return content || "";
 		}
@@ -1133,9 +1092,9 @@ class Processor {
 	insertShadowRootContents() {
 		const doc = this.doc;
 		const options = this.options;
-		if (this.options.shadowRoots && this.options.shadowRoots.length) {
+		if (options.shadowRoots && options.shadowRoots.length) {
 			processElement(this.doc);
-			if (this.options.removeScripts) {
+			if (options.blockScripts) {
 				this.doc.querySelectorAll("script[" + SCRIPT_TEMPLATE_SHADOW_ROOT + "]").forEach(element => element.remove());
 			}
 			const scriptElement = doc.createElement("script");
@@ -1272,19 +1231,18 @@ class Processor {
 			["image", "xlink:href"],
 			["image", "href"]
 		];
+		if (this.options.blockImages) {
+			this.doc.querySelectorAll("svg").forEach(element => element.remove());
+		}
 		let resourcePromises = processAttributeArgs.map(([selector, attributeName, processDuplicates, removeElementIfMissing]) =>
 			ProcessorHelper.processAttribute(this.doc.querySelectorAll(selector), attributeName, this.baseURI, this.options, "image", this.cssVariables, this.styles, this.batchRequest, processDuplicates, removeElementIfMissing)
 		);
 		resourcePromises = resourcePromises.concat([
 			ProcessorHelper.processXLinks(this.doc.querySelectorAll("use"), this.doc, this.baseURI, this.options, this.batchRequest),
-			ProcessorHelper.processSrcset(this.doc.querySelectorAll("img[srcset], source[srcset]"), "srcset", this.baseURI, this.batchRequest)
+			ProcessorHelper.processSrcset(this.doc.querySelectorAll("img[srcset], source[srcset]"), this.baseURI, this.options, this.batchRequest)
 		]);
-		if (!this.options.removeAudioSrc) {
-			resourcePromises.push(ProcessorHelper.processAttribute(this.doc.querySelectorAll("audio[src], audio > source[src]"), "src", this.baseURI, this.options, "audio", this.cssVariables, this.styles, this.batchRequest));
-		}
-		if (!this.options.removeVideoSrc) {
-			resourcePromises.push(ProcessorHelper.processAttribute(this.doc.querySelectorAll("video[src], video > source[src]"), "src", this.baseURI, this.options, "video", this.cssVariables, this.styles, this.batchRequest));
-		}
+		resourcePromises.push(ProcessorHelper.processAttribute(this.doc.querySelectorAll("audio[src], audio > source[src]"), "src", this.baseURI, this.options, "audio", this.cssVariables, this.styles, this.batchRequest));
+		resourcePromises.push(ProcessorHelper.processAttribute(this.doc.querySelectorAll("video[src], video > source[src]"), "src", this.baseURI, this.options, "video", this.cssVariables, this.styles, this.batchRequest));
 		await Promise.all(resourcePromises);
 		if (this.options.saveFavicon) {
 			ProcessorHelper.processShortcutIcons(this.doc);
@@ -1292,60 +1250,46 @@ class Processor {
 	}
 
 	async processScripts() {
-		await Promise.all(Array.from(this.doc.querySelectorAll("script[src], link[rel=preload][as=script][href]")).map(async element => {
+		await Promise.all(Array.from(this.doc.querySelectorAll("script[src]")).map(async element => {
 			let resourceURL;
 			let scriptSrc;
-			if (element.tagName == "SCRIPT") {
-				scriptSrc = element.getAttribute("src");
-				if (this.options.saveOriginalURLs && !isDataURL(scriptSrc)) {
-					element.setAttribute("data-sf-original-src", scriptSrc);
-				}
-			} else {
-				scriptSrc = element.getAttribute("href");
-				if (this.options.saveOriginalURLs && !isDataURL(scriptSrc)) {
-					element.setAttribute("data-sf-original-href", scriptSrc);
-				}
+			scriptSrc = element.getAttribute("src");
+			if (this.options.saveOriginalURLs && !isDataURL(scriptSrc)) {
+				element.setAttribute("data-sf-original-src", scriptSrc);
 			}
 			element.removeAttribute("integrity");
-			element.textContent = "";
-			try {
-				resourceURL = util.resolveURL(scriptSrc, this.baseURI);
-			} catch (error) {
-				// ignored
-			}
-			if (testValidURL(resourceURL)) {
-				if (element.tagName == "SCRIPT") {
-					element.removeAttribute("src");
-				} else {
-					element.removeAttribute("href");
+			if (!this.options.blockScripts) {
+				element.textContent = "";
+				try {
+					resourceURL = util.resolveURL(scriptSrc, this.baseURI);
+				} catch (error) {
+					// ignored
 				}
-				this.stats.add("processed", "scripts", 1);
-				const content = await util.getContent(resourceURL, {
-					asBinary: true,
-					charset: this.charset != UTF8_CHARSET && this.charset,
-					maxResourceSize: this.options.maxResourceSize,
-					maxResourceSizeEnabled: this.options.maxResourceSizeEnabled,
-					frameId: this.options.windowId,
-					resourceReferrer: this.options.resourceReferrer,
-					baseURI: this.options.baseURI,
-					blockMixedContent: this.options.blockMixedContent,
-					expectedType: "script",
-					acceptHeaders: this.options.acceptHeaders,
-					networkTimeout: this.options.networkTimeout
-				});
-				content.data = getUpdatedResourceContent(resourceURL, content, this.options);
-				if (element.tagName == "SCRIPT") {
+				if (testValidURL(resourceURL)) {
+					element.removeAttribute("src");
+					const content = await util.getContent(resourceURL, {
+						asBinary: true,
+						charset: this.charset != UTF8_CHARSET && this.charset,
+						maxResourceSize: this.options.maxResourceSize,
+						maxResourceSizeEnabled: this.options.maxResourceSizeEnabled,
+						frameId: this.options.windowId,
+						resourceReferrer: this.options.resourceReferrer,
+						baseURI: this.options.baseURI,
+						blockMixedContent: this.options.blockMixedContent,
+						expectedType: "script",
+						acceptHeaders: this.options.acceptHeaders,
+						networkTimeout: this.options.networkTimeout
+					});
+					content.data = getUpdatedResourceContent(resourceURL, content, this.options);
 					element.setAttribute("src", content.data);
-					if (element.getAttribute("async") == "" || element.getAttribute("async") == "async" || element.getAttribute(util.ASYNC_SCRIPT_ATTRIBUTE_NAME) == "") {
+					if (element.getAttribute("async") == "async" || element.getAttribute(util.ASYNC_SCRIPT_ATTRIBUTE_NAME) == "") {
 						element.setAttribute("async", "");
 					}
-				} else {
-					const scriptElement = this.doc.createElement("script");
-					scriptElement.setAttribute("src", content.data);
-					scriptElement.setAttribute("async", "");
-					element.parentElement.replaceChild(scriptElement, element);
 				}
+			} else {
+				element.removeAttribute("src");
 			}
+			this.stats.add("processed", "scripts", 1);
 		}));
 	}
 
@@ -1580,7 +1524,6 @@ class Processor {
 // ---------------
 const DATA_URI_PREFIX = "data:";
 const ABOUT_BLANK_URI = "about:blank";
-const EMPTY_DATA_URI = "data:null;base64,";
 const REGEXP_URL_HASH = /(#.+?)$/;
 const SINGLE_FILE_VARIABLE_NAME_PREFIX = "--sf-img-";
 const SINGLE_FILE_VARIABLE_MAX_SIZE = 512 * 1024;
@@ -1819,9 +1762,9 @@ class ProcessorHelper {
 				} else {
 					let newUrlFunction;
 					if (originalResourceURL) {
-						newUrlFunction = urlFunction.replace(originalResourceURL, EMPTY_DATA_URI);
+						newUrlFunction = urlFunction.replace(originalResourceURL, util.EMPTY_RESOURCE);
 					} else {
-						newUrlFunction = "url(" + EMPTY_DATA_URI + ")";
+						newUrlFunction = "url(" + util.EMPTY_RESOURCE + ")";
 					}
 					stylesheetContent = stylesheetContent.replace(getRegExp(urlFunction), newUrlFunction);
 				}
@@ -1890,9 +1833,9 @@ class ProcessorHelper {
 					const urlFunctions = getUrlFunctions(getCSSValue(declaration.value), true);
 					await Promise.all(urlFunctions.map(async urlFunction => {
 						const originalResourceURL = matchURL(urlFunction);
-						const resourceURL = normalizeURL(originalResourceURL);
-						if (!testIgnoredPath(resourceURL)) {
-							if (testValidURL(resourceURL)) {
+						if (!options.blockFonts) {
+							const resourceURL = normalizeURL(originalResourceURL);
+							if (!testIgnoredPath(resourceURL) && testValidURL(resourceURL)) {
 								let { content } = await batchRequest.addURL(resourceURL,
 									{ asBinary: true, expectedType: "font", baseURI, blockMixedContent: options.blockMixedContent });
 								let resourceURLs = options.fontDeclarations.get(declaration);
@@ -1903,6 +1846,8 @@ class ProcessorHelper {
 								resourceURLs.push(resourceURL);
 								replaceURLs(declaration, originalResourceURL, content);
 							}
+						} else {
+							replaceURLs(declaration, originalResourceURL, util.EMPTY_RESOURCE);
 						}
 					}));
 				}
@@ -1931,9 +1876,9 @@ class ProcessorHelper {
 				const urlFunctions = getUrlFunctions(getCSSValue(declaration.value));
 				await Promise.all(urlFunctions.map(async urlFunction => {
 					const originalResourceURL = matchURL(urlFunction);
-					const resourceURL = normalizeURL(originalResourceURL);
-					if (!testIgnoredPath(resourceURL)) {
-						if (testValidURL(resourceURL)) {
+					if (!options.blockImages) {
+						const resourceURL = normalizeURL(originalResourceURL);
+						if (!testIgnoredPath(resourceURL) && testValidURL(resourceURL)) {
 							let { content, indexResource, duplicate } = await batchRequest.addURL(resourceURL,
 								{ asBinary: true, expectedType: "image", groupDuplicates: options.groupDuplicateImages });
 							let variableDefined;
@@ -1954,6 +1899,8 @@ class ProcessorHelper {
 								tokens.forEach(({ parent, token, value }) => parent.replace(token, value));
 							}
 						}
+					} else {
+						findURLToken(originalResourceURL, declaration.value.children, token => token.data.value.value = util.EMPTY_RESOURCE);
 					}
 				}));
 			}
@@ -1981,62 +1928,66 @@ class ProcessorHelper {
 					resourceElement.setAttribute("data-sf-original-" + attributeName, resourceURL);
 				}
 				delete resourceElement.dataset.singleFileOriginURL;
-				if (!testIgnoredPath(resourceURL)) {
-					resourceElement.setAttribute(attributeName, EMPTY_IMAGE);
-					if (testValidPath(resourceURL)) {
-						try {
-							resourceURL = util.resolveURL(resourceURL, baseURI);
-						} catch (error) {
-							// ignored
-						}
-						if (testValidURL(resourceURL)) {
-							let { content, indexResource, duplicate } = await batchRequest.addURL(resourceURL,
-								{ asBinary: true, expectedType, groupDuplicates: options.groupDuplicateImages && resourceElement.tagName == "IMG" && attributeName == "src" });
-							if (originURL) {
-								if (content == EMPTY_DATA_URI) {
-									try {
-										originURL = util.resolveURL(originURL, baseURI);
-									} catch (error) {
-										// ignored
-									}
-									try {
-										resourceURL = originURL;
-										content = (await util.getContent(resourceURL, {
-											asBinary: true,
-											expectedType: "image",
-											maxResourceSize: options.maxResourceSize,
-											maxResourceSizeEnabled: options.maxResourceSizeEnabled,
-											frameId: options.windowId,
-											resourceReferrer: options.resourceReferrer,
-											acceptHeaders: options.acceptHeaders,
-											networkTimeout: options.networkTimeout
-										})).data;
-									} catch (error) {
-										// ignored
+				if (!options["block" + expectedType.charAt(0).toUpperCase() + expectedType.substring(1) + "s"]) {
+					if (!testIgnoredPath(resourceURL)) {
+						resourceElement.setAttribute(attributeName, util.EMPTY_RESOURCE);
+						if (testValidPath(resourceURL)) {
+							try {
+								resourceURL = util.resolveURL(resourceURL, baseURI);
+							} catch (error) {
+								// ignored
+							}
+							if (testValidURL(resourceURL)) {
+								let { content, indexResource, duplicate } = await batchRequest.addURL(resourceURL,
+									{ asBinary: true, expectedType, groupDuplicates: options.groupDuplicateImages && resourceElement.tagName == "IMG" && attributeName == "src" });
+								if (originURL) {
+									if (content == util.EMPTY_RESOURCE) {
+										try {
+											originURL = util.resolveURL(originURL, baseURI);
+										} catch (error) {
+											// ignored
+										}
+										try {
+											resourceURL = originURL;
+											content = (await util.getContent(resourceURL, {
+												asBinary: true,
+												expectedType,
+												maxResourceSize: options.maxResourceSize,
+												maxResourceSizeEnabled: options.maxResourceSizeEnabled,
+												frameId: options.windowId,
+												resourceReferrer: options.resourceReferrer,
+												acceptHeaders: options.acceptHeaders,
+												networkTimeout: options.networkTimeout
+											})).data;
+										} catch (error) {
+											// ignored
+										}
 									}
 								}
-							}
-							if (removeElementIfMissing && content == EMPTY_DATA_URI) {
-								resourceElement.remove();
-							} else if (content !== EMPTY_DATA_URI) {
-								const forbiddenPrefixFound = PREFIXES_FORBIDDEN_DATA_URI.filter(prefixDataURI => content.startsWith(prefixDataURI)).length;
-								if (!forbiddenPrefixFound) {
-									const isSVG = content.startsWith(PREFIX_DATA_URI_IMAGE_SVG);
-									if (processDuplicates && duplicate && options.groupDuplicateImages && !isSVG && util.getContentSize(content) < SINGLE_FILE_VARIABLE_MAX_SIZE) {
-										if (ProcessorHelper.replaceImageSource(resourceElement, SINGLE_FILE_VARIABLE_NAME_PREFIX + indexResource, options)) {
-											cssVariables.set(indexResource, { content, url: originURL });
-											const declarationList = cssTree.parse(resourceElement.getAttribute("style"), { context: "declarationList" });
-											styles.set(resourceElement, declarationList);
+								if (removeElementIfMissing && content == util.EMPTY_RESOURCE) {
+									resourceElement.remove();
+								} else if (content !== util.EMPTY_RESOURCE) {
+									const forbiddenPrefixFound = PREFIXES_FORBIDDEN_DATA_URI.filter(prefixDataURI => content.startsWith(prefixDataURI)).length;
+									if (!forbiddenPrefixFound) {
+										const isSVG = content.startsWith(PREFIX_DATA_URI_IMAGE_SVG);
+										if (expectedType == "image" && processDuplicates && duplicate && !isSVG && util.getContentSize(content) < SINGLE_FILE_VARIABLE_MAX_SIZE) {
+											if (ProcessorHelper.replaceImageSource(resourceElement, SINGLE_FILE_VARIABLE_NAME_PREFIX + indexResource, options)) {
+												cssVariables.set(indexResource, { content, url: originURL });
+												const declarationList = cssTree.parse(resourceElement.getAttribute("style"), { context: "declarationList" });
+												styles.set(resourceElement, declarationList);
+											} else {
+												resourceElement.setAttribute(attributeName, content);
+											}
 										} else {
 											resourceElement.setAttribute(attributeName, content);
 										}
-									} else {
-										resourceElement.setAttribute(attributeName, content);
 									}
 								}
 							}
 						}
 					}
+				} else {
+					resourceElement.setAttribute(attributeName, util.EMPTY_RESOURCE);
 				}
 			}
 		}));
@@ -2054,75 +2005,83 @@ class ProcessorHelper {
 				resourceElement.setAttribute("data-sf-original-href", originalResourceURL);
 			}
 			let resourceURL = normalizeURL(originalResourceURL);
-			if (testValidPath(resourceURL) && !testIgnoredPath(resourceURL)) {
-				resourceElement.setAttribute(attributeName, EMPTY_IMAGE);
-				try {
-					resourceURL = util.resolveURL(resourceURL, baseURI);
-				} catch (error) {
-					// ignored
-				}
-				if (testValidURL(resourceURL)) {
-					const hashMatch = originalResourceURL.match(REGEXP_URL_HASH);
-					if (originalResourceURL.startsWith(baseURI + "#")) {
-						resourceElement.setAttribute(attributeName, hashMatch[0]);
-					} else {
-						const response = await batchRequest.addURL(resourceURL, { expectedType: "image" });
-						const svgDoc = util.parseSVGContent(response.content);
-						if (hashMatch && hashMatch[0]) {
-							let symbolElement;
-							try {
-								symbolElement = svgDoc.querySelector(hashMatch[0]);
-							} catch (error) {
-								// ignored
-							}
-							if (symbolElement) {
-								resourceElement.setAttribute(attributeName, hashMatch[0]);
-								resourceElement.parentElement.insertBefore(symbolElement, resourceElement.parentElement.firstChild);
-							}
+			if (!options.blockImages) {
+				if (testValidPath(resourceURL) && !testIgnoredPath(resourceURL)) {
+					resourceElement.setAttribute(attributeName, util.EMPTY_RESOURCE);
+					try {
+						resourceURL = util.resolveURL(resourceURL, baseURI);
+					} catch (error) {
+						// ignored
+					}
+					if (testValidURL(resourceURL)) {
+						const hashMatch = originalResourceURL.match(REGEXP_URL_HASH);
+						if (originalResourceURL.startsWith(baseURI + "#")) {
+							resourceElement.setAttribute(attributeName, hashMatch[0]);
 						} else {
-							const content = await batchRequest.addURL(resourceURL, { expectedType: "image" });
-							resourceElement.setAttribute(attributeName, PREFIX_DATA_URI_IMAGE_SVG + "," + content);
+							const response = await batchRequest.addURL(resourceURL, { expectedType: "image" });
+							const svgDoc = util.parseSVGContent(response.content);
+							if (hashMatch && hashMatch[0]) {
+								let symbolElement;
+								try {
+									symbolElement = svgDoc.querySelector(hashMatch[0]);
+								} catch (error) {
+									// ignored
+								}
+								if (symbolElement) {
+									resourceElement.setAttribute(attributeName, hashMatch[0]);
+									resourceElement.parentElement.insertBefore(symbolElement, resourceElement.parentElement.firstChild);
+								}
+							} else {
+								const content = await batchRequest.addURL(resourceURL, { expectedType: "image" });
+								resourceElement.setAttribute(attributeName, PREFIX_DATA_URI_IMAGE_SVG + "," + content);
+							}
 						}
 					}
+				} else if (resourceURL == options.url) {
+					resourceElement.setAttribute(attributeName, originalResourceURL.substring(resourceURL.length));
 				}
-			} else if (resourceURL == options.url) {
-				resourceElement.setAttribute(attributeName, originalResourceURL.substring(resourceURL.length));
+			} else {
+				resourceElement.setAttribute(attributeName, util.EMPTY_RESOURCE);
 			}
 		}));
 	}
 
-	static async processSrcset(resourceElements, attributeName, baseURI, batchRequest) {
+	static async processSrcset(resourceElements, baseURI, options, batchRequest) {
 		await Promise.all(Array.from(resourceElements).map(async resourceElement => {
-			const originSrcset = resourceElement.getAttribute(attributeName);
+			const originSrcset = resourceElement.getAttribute("srcset");
 			const srcset = util.parseSrcset(originSrcset);
 			resourceElement.setAttribute("data-sf-original-srcset", originSrcset);
-			const srcsetValues = await Promise.all(srcset.map(async srcsetValue => {
-				let resourceURL = normalizeURL(srcsetValue.url);
-				if (!testIgnoredPath(resourceURL)) {
-					if (testValidPath(resourceURL)) {
-						try {
-							resourceURL = util.resolveURL(resourceURL, baseURI);
-						} catch (error) {
-							// ignored
-						}
-						if (testValidURL(resourceURL)) {
-							const { content } = await batchRequest.addURL(resourceURL, { asBinary: true, expectedType: "image" });
-							const forbiddenPrefixFound = PREFIXES_FORBIDDEN_DATA_URI.filter(prefixDataURI => content.startsWith(prefixDataURI)).length;
-							if (forbiddenPrefixFound) {
+			if (!options.blockImages) {
+				const srcsetValues = await Promise.all(srcset.map(async srcsetValue => {
+					let resourceURL = normalizeURL(srcsetValue.url);
+					if (!testIgnoredPath(resourceURL)) {
+						if (testValidPath(resourceURL)) {
+							try {
+								resourceURL = util.resolveURL(resourceURL, baseURI);
+							} catch (error) {
+								// ignored
+							}
+							if (testValidURL(resourceURL)) {
+								const { content } = await batchRequest.addURL(resourceURL, { asBinary: true, expectedType: "image" });
+								const forbiddenPrefixFound = PREFIXES_FORBIDDEN_DATA_URI.filter(prefixDataURI => content.startsWith(prefixDataURI)).length;
+								if (forbiddenPrefixFound) {
+									return "";
+								}
+								return content + (srcsetValue.w ? " " + srcsetValue.w + "w" : srcsetValue.d ? " " + srcsetValue.d + "x" : "");
+							} else {
 								return "";
 							}
-							return content + (srcsetValue.w ? " " + srcsetValue.w + "w" : srcsetValue.d ? " " + srcsetValue.d + "x" : "");
 						} else {
 							return "";
 						}
 					} else {
-						return "";
+						return resourceURL + (srcsetValue.w ? " " + srcsetValue.w + "w" : srcsetValue.d ? " " + srcsetValue.d + "x" : "");
 					}
-				} else {
-					return resourceURL + (srcsetValue.w ? " " + srcsetValue.w + "w" : srcsetValue.d ? " " + srcsetValue.d + "x" : "");
-				}
-			}));
-			resourceElement.setAttribute(attributeName, srcsetValues.join(", "));
+				}));
+				resourceElement.setAttribute("srcset", srcsetValues.join(", "));
+			} else {
+				resourceElement.setAttribute("srcset", "");
+			}
 		}));
 	}
 
@@ -2303,7 +2262,7 @@ function getImportFunctions(stylesheetContent) {
 }
 
 function findShortcutIcon(shortcutIcons) {
-	shortcutIcons = shortcutIcons.filter(linkElement => linkElement.href != EMPTY_IMAGE);
+	shortcutIcons = shortcutIcons.filter(linkElement => linkElement.href != util.EMPTY_RESOURCE);
 	shortcutIcons.sort((linkElement1, linkElement2) => (parseInt(linkElement2.sizes, 10) || 16) - (parseInt(linkElement1.sizes, 10) || 16));
 	return shortcutIcons[0];
 }

+ 4 - 2
src/single-file/single-file-helper.js

@@ -61,6 +61,7 @@ const FONT_WEIGHTS = {
 const COMMENT_HEADER = "Page saved with SingleFile";
 const COMMENT_HEADER_LEGACY = "Archive processed by SingleFile";
 const SINGLE_FILE_UI_ELEMENT_CLASS = "single-file-ui-element";
+const EMPTY_RESOURCE = "data:,";
 const addEventListener = (type, listener, options) => globalThis.addEventListener(type, listener, options);
 const dispatchEvent = event => globalThis.dispatchEvent(event);
 
@@ -96,7 +97,8 @@ export {
 	ASYNC_SCRIPT_ATTRIBUTE_NAME,
 	COMMENT_HEADER,
 	COMMENT_HEADER_LEGACY,
-	SINGLE_FILE_UI_ELEMENT_CLASS
+	SINGLE_FILE_UI_ELEMENT_CLASS,
+	EMPTY_RESOURCE
 };
 
 function initUserScriptHandler() {
@@ -261,7 +263,7 @@ function getResourcesInfo(win, doc, element, options, data, elementHidden, compu
 	if (element.tagName == "IMG") {
 		const imageData = {
 			currentSrc: elementHidden ?
-				"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" :
+				EMPTY_RESOURCE :
 				(options.loadDeferredImages && element.getAttribute(LAZY_SRC_ATTRIBUTE_NAME)) || element.currentSrc
 		};
 		data.images.push(imageData);

+ 4 - 3
src/single-file/single-file-util.js

@@ -185,7 +185,8 @@ function getInstance(utilOptions) {
 		SELECTED_CONTENT_ATTRIBUTE_NAME: helper.SELECTED_CONTENT_ATTRIBUTE_NAME,
 		COMMENT_HEADER: helper.COMMENT_HEADER,
 		COMMENT_HEADER_LEGACY: helper.COMMENT_HEADER_LEGACY,
-		SINGLE_FILE_UI_ELEMENT_CLASS: helper.SINGLE_FILE_UI_ELEMENT_CLASS
+		SINGLE_FILE_UI_ELEMENT_CLASS: helper.SINGLE_FILE_UI_ELEMENT_CLASS,
+		EMPTY_RESOURCE: helper.EMPTY_RESOURCE
 	};
 
 	async function getContent(resourceURL, options) {
@@ -241,7 +242,7 @@ function getInstance(utilOptions) {
 		try {
 			buffer = await response.arrayBuffer();
 		} catch (error) {
-			return { data: options.asBinary ? "data:null;base64," : "", resourceURL };
+			return { data: options.asBinary ? helper.EMPTY_RESOURCE : "", resourceURL };
 		}
 		resourceURL = response.url || resourceURL;
 		let contentType = "", charset;
@@ -323,7 +324,7 @@ async function getFetchResponse(resourceURL, options, data, charset, contentType
 			}
 		}
 	} else {
-		data = options.asBinary ? "data:null;base64" : "";
+		data = options.asBinary ? helper.EMPTY_RESOURCE : "";
 	}
 	return { data, resourceURL, charset };
 }