A kind reminder we would like all registered users to weigh in on one of our forum's security policies.
Please take a moment to read this thread and place a vote.
https://forum.palemoon.org/viewtopic.php?f=17&t=32935

Intercept & Modify HTTP Response

Dedicated board for extension releases/support threads

Moderators: FranklinDM, Lootyhoof

Forum rules
Please do not create new topics here unless you are an extension author in need of a dedicated releases&support thread!
User avatar
tellu-white
Lunatic
Lunatic
Posts: 274
Joined: 2022-03-08, 22:02

Intercept & Modify HTTP Response

Post by tellu-white » 2025-11-06, 09:36

****************************************************************************

HELP Page for this add-on:

https://forum.palemoon.org/viewtopic.php?f=70&t=31829


****************************************************************************

I've made a new version (4.5) of this add-on where I fixed a bug in the function ("parseReg") that returns a RegEx "object" if the function argument (a "string") has valid RegEx syntax. In the old version, this function produced an error in a particular case: when the string (the function argument) started with a forward slash and there was a backslash before the last forward slash in the string (see also the code posted below - with comments).

The new function, with comments attached:

Code: Select all


function parseReg(src){
	try{
		// Old "match":
		// var match = src.match(/^\/(.+)\/([igm]{0,3})$/);		// Without "Negative LookBehind" in RegEx, an ERROR occurs (err.message = \ at end of pattern) for src=/xxxxx\/g (for example)
		
		// New "match":
		var match = src.match(/^\/(.+)(?<!\\)\/([igm]{0,3})$/);		// With "Negative LookBehind" in RegEx, this Function Returns a String if Before the LAST / there is \  -->  src=/xxxxx\/g returns a String, NOT a RegEx (for example)
		
		if(match) {
			return new RegExp(match[1],match[2]);
			
		} else{
			return src;
		}
		
	} catch(err){
		// alert(err.message);
		
		return src;		// Just in case, so that the Add-On does not get stuck  -->  An unforeseen error in the RegEx (string) syntax can be seen (examined) by opening "Filters Manager"
	}
}

Download link ( Intercept & Modify HTTP Response 4.5 ):

https://www.mediafire.com/file/61kuzhz9tikcm3p/intercept_&_modify_HTTP_response_4_5.zip/file

User avatar
frostknight
Keeps coming back
Keeps coming back
Posts: 773
Joined: 2022-08-10, 02:25

Re: Intercept & Modify HTTP Response

Post by frostknight » 2025-11-06, 18:59

Recommend not using mediafire and instead using upload.disroot.org

Mediafire gave me a hard time and refused to show the download to me no matter how many times I refreshed.

They are doing some chrome level crap right now.

Ultimately I had to use badwolf browser to download it. Really annoying...
Freedom is never more than one generation away from extinction. Feelings are not facts
If you wish to be humbled, try to exalt yourself long term If you wish to be exalted, try to humble yourself long term
Favourite operating systems: Hyperbola Devuan OpenBSD
Say NO to Fascism and Corporatism as much as possible!
Also, Peace Be With us All!

User avatar
tellu-white
Lunatic
Lunatic
Posts: 274
Joined: 2022-03-08, 22:02

Re: Intercept & Modify HTTP Response

Post by tellu-white » 2025-11-06, 23:14

frostknight wrote:
2025-11-06, 18:59
Recommend not using mediafire and instead using upload.disroot.org

Mediafire gave me a hard time and refused to show the download to me no matter how many times I refreshed.

They are doing some chrome level crap right now.

Ultimately I had to use badwolf browser to download it. Really annoying...
I checked the page https://upload.disroot.org/ and saw that they keep uploaded files for a maximum of 30 days.

https://www.mediafire.com/ also has a time limit, but it is very flexible and depends on the existing demand for the uploaded file. For example, my add-on "Intercept & Modify HTTP Response 4.3" is still available today, 10 months after I uploaded it (on January 7, 2025). I also test the availability of every file uploaded to "mediafire.com" and I've never had an experience like yours.

Screenshots:

"upload.disroot.org":
upload.disroot.png
"mediafire.com":
mediafire_01.png
mediafire_02.png
mediafire_03.png
You do not have the required permissions to view the files attached to this post.

User avatar
frostknight
Keeps coming back
Keeps coming back
Posts: 773
Joined: 2022-08-10, 02:25

Re: Intercept & Modify HTTP Response

Post by frostknight » 2025-11-08, 21:00

tellu-white wrote:
2025-11-06, 23:14
I checked the page https://upload.disroot.org/ and saw that they keep uploaded files for a maximum of 30 days.

https://www.mediafire.com/ also has a time limit, but it is very flexible and depends on the existing demand for the uploaded file. For example, my add-on "Intercept & Modify HTTP Response 4.3" is still available today, 10 months after I uploaded it (on January 7, 2025). I also test the availability of every file uploaded to "mediafire.com" and I've never had an experience like yours.
Fair point, I forgot about the 30 days thing.

Yeah, that is the first time I had that problem with mediafire. Very peculiar...

Still doing it... weird stuff
Freedom is never more than one generation away from extinction. Feelings are not facts
If you wish to be humbled, try to exalt yourself long term If you wish to be exalted, try to humble yourself long term
Favourite operating systems: Hyperbola Devuan OpenBSD
Say NO to Fascism and Corporatism as much as possible!
Also, Peace Be With us All!

dinosaur
Fanatic
Fanatic
Posts: 190
Joined: 2014-06-03, 09:26
Location: France

Re: Intercept & Modify HTTP Response

Post by dinosaur » 2025-11-18, 11:42

How can I migrate mhresponse v1.3.8 filters to your version without having to add the filters host-per-host in you new extension ???

User avatar
tellu-white
Lunatic
Lunatic
Posts: 274
Joined: 2022-03-08, 22:02

Re: Intercept & Modify HTTP Response

Post by tellu-white » 2025-11-18, 19:48

dinosaur wrote:
2025-11-18, 11:42
How can I migrate mhresponse v1.3.8 filters to your version without having to add the filters host-per-host in you new extension ???
HELP to Transfer ALL Existing Filters from Modify "HTTP Response 1.3.8 (JustOff)" add-on to "Intercept & Modify HTTP Response 4.5 (Tellu White)" add-on:

https://forum.palemoon.org/viewtopic.php?f=70&t=31829#p257566

Note: After you've copied the filters from "Modify "HTTP Response 1.3.8" (see step 1 from Screenshots in the help opened with the link above), it would be a good idea to save them in a text file as well, in case you want to use "HTTP Response 1.3.8 (JustOff)" again instead of "Intercept & Modify HTTP Response 4.5 (Tellu White)".

dinosaur
Fanatic
Fanatic
Posts: 190
Joined: 2014-06-03, 09:26
Location: France

Re: Intercept & Modify HTTP Response

Post by dinosaur » 2025-11-19, 14:14

tellu-white wrote:
2025-11-18, 19:48
HELP to Transfer ALL Existing Filters from Modify "HTTP Response 1.3.8 (JustOff)" add-on to "Intercept & Modify HTTP Response 4.5 (Tellu White)" add-on:
Thanks.

It worked, but I find the procedure a bit clunky (and quite unintuitive): why using the clipboard as an intermediary between the conversion and the enrollment of the filters ?

User avatar
tellu-white
Lunatic
Lunatic
Posts: 274
Joined: 2022-03-08, 22:02

Re: Intercept & Modify HTTP Response

Post by tellu-white » 2025-11-19, 15:33

dinosaur wrote:
2025-11-19, 14:14
It worked, but I find the procedure a bit clunky (and quite unintuitive): why using the clipboard as an intermediary between the conversion and the enrollment of the filters ?
This add-on has three options for adding filters:

1. Using the "Filters Manager," which can be opened with click on the add-on Button (not relevant in this particular case).

2. With right-click on the add-on Button + "Restore Filters From Backup (Replaces Existing Filters!)"

3. With right-click on the add-on Button + "Add Filters From Backup (To Current Filters)"

So, after the user makes the filters conversion, he/she has two options:

A. To replace all existing filters.

B. To add the new filters to the existing ones.

I could not know which option the user would choose, so I chose to save the converted filters to the Clipboard. I could have created an additional pop-up dialog box where the user could check the desired option. I chose the Clipboard option because it also allows you to save filters in a text file (backup).

Screenshot:
01.png
You do not have the required permissions to view the files attached to this post.

User avatar
tellu-white
Lunatic
Lunatic
Posts: 274
Joined: 2022-03-08, 22:02

Re: Intercept & Modify HTTP Response

Post by tellu-white » 2025-12-09, 17:23

I posted above the link to the HELP page for this add-on ( https://forum.palemoon.org/viewtopic.php?f=70&t=31829 ). As for me, I saved the three help pages from there as well as this page on my hard drive. To do this, I created a Custom Button, because "File / Save Page As" in Pale Moon only saves the HTML file (without CSS and without image files), and the "ScrapBook Plus" add-on does not save image files, so these will be opened from the forum when you click on them (on their links).

The Custom Button code (which is only for Windows - my operating system):

Code: Select all

// Download Pale Moon Forum Page

/*CODE*/

///////////////////////////////////////  START - Restore "src" attribute of "img" TAGs  ///////////////////////////////////////

function restore_src_attribute_of_img_TAGs_in_Pale_Moon_Forum_page(){
	
	// Replace "amp-img" tags with "img" tags
	
	content.document.querySelectorAll("amp-img").forEach(amp_img_tag => {
		var amp_img_tag_width_height;
		
		var amp_img_tag_width;
		var amp_img_tag_height;
		
		var amp_img__src;
		var amp_img__srcset;
			
		amp_img_tag_width_height = amp_img_tag.getBoundingClientRect();
		
		if(amp_img_tag_width_height){
			amp_img_tag_width = parseInt(amp_img_tag_width_height.width);
			amp_img_tag_height = parseInt(amp_img_tag_width_height.height);
		}
		
		for (var att, y = 0, atts = amp_img_tag.attributes, n = atts.length; y < n; y++){
			att = atts[y];
			
			if(att.name == "src"){
				amp_img__src = att.value;
			}
			
			if(att.name == "srcset"){
				amp_img__srcset = att.value;
			}
		}
		
		var img_tag = amp_img_tag.getElementsByTagName("img");
		
		if(img_tag.length > 0){
			for(var x = 0; x < img_tag.length; x++) {
				if(img_tag[x].parentNode && img_tag[x].parentNode.tagName == "AMP-IMG"){
					var cloned_img_tag = img_tag[x].cloneNode();
					
					cloned_img_tag.removeAttribute("class");
					
					if(amp_img_tag_width){
						cloned_img_tag.width = amp_img_tag_width;
						cloned_img_tag.setAttribute("height", "auto");
						
					} else {
						cloned_img_tag.setAttribute("width", "100%");
						cloned_img_tag.setAttribute("height", "auto");
					}
					
					cloned_img_tag.className = "img__from__amp_img";
				}
				
				try{
					amp_img_tag.parentNode.insertBefore(cloned_img_tag, amp_img_tag);
				
				} catch(err){
					// alert(err.message);
				}
			}
			
			try{
				amp_img_tag.parentNode.removeChild(amp_img_tag);
				
			} catch(err){
				// alert(err.message);
			}
			
		} else{
			amp_img_tag.removeAttribute("class");
			
			var new_img_tag = content.document.createElement("img");
			
			if(amp_img__src){
				new_img_tag.src = amp_img__src;
			}
			
			if(amp_img__srcset){
				new_img_tag.srcset = amp_img__srcset;
			}
			
			if(amp_img_tag_width){
				new_img_tag.width = amp_img_tag_width;
				new_img_tag.setAttribute("height", "auto");
				
			} else {
				new_img_tag.setAttribute("width", "100%");				
				new_img_tag.setAttribute("height", "auto");
			}
			
			new_img_tag.className = "img__from__amp_img";
			
			try{
				amp_img_tag.parentNode.insertBefore(new_img_tag, amp_img_tag);
			
			} catch(err){
				// alert(err.message);
			}
			
			try{
				amp_img_tag.parentNode.removeChild(amp_img_tag);
				
			} catch(err){
				// alert(err.message);
			}
		}
	});
	
	// Replace the "noscript" TAGs with the "img" TAG code from within its "textContent",
	// if it exists and if there is no "img" TAG in the "parent" of the "noscript" TAG.
	
	content.document.querySelectorAll("noscript").forEach(noscript_tag => {
		var noscript_tag__parentNode = noscript_tag.parentNode;		
		var img_tags_in_parent_of_noscript_tag = noscript_tag__parentNode.getElementsByTagName("img");
		
		if(img_tags_in_parent_of_noscript_tag.length > 0){
			return;
		}
		
		var noScript_textContent = noscript_tag.textContent;		
		var obj_noScript_inner_HTML_tags = HTML_string_to_DOM_object_for_Google(noScript_textContent);		
		var img_tag = obj_noScript_inner_HTML_tags.getElementsByTagName("img")[0];
		
		if(img_tag){
			var img_tag_width = img_tag.width;
			var img_tag_height = img_tag.height;
			
			if((img_tag_width && img_tag_width < 5) || (img_tag_height && img_tag_height < 5)){
				return;
			}
			
			var img_src = img_tag.src;
			var img_srcset = img_tag.srcset;

			var img_tag_width_height__from_grandparent;
			
			var img_tag_width__from_grandparent;
			var img_tag_height__from_grandparent;
			
			try{
				img_tag_width_height__from_grandparent = noscript_tag__parentNode.getBoundingClientRect();
				
				if(img_tag_width_height__from_grandparent){
					img_tag_width__from_grandparent = parseInt(img_tag_width_height__from_grandparent.width);
					img_tag_height__from_grandparent = parseInt(img_tag_width_height__from_grandparent.height);
				}
				
			} catch(err){
				// alert(err.message);
			}
			
			noscript_tag__parentNode.removeAttribute("class");
			
			var new_img_tag = content.document.createElement("img");			
			new_img_tag.className = "img__from__noscript";
			
			if(img_src){
				new_img_tag.src = img_src;
			}
			
			if(img_srcset){
				new_img_tag.srcset = img_srcset;
			}
			
			if(img_tag_width__from_grandparent){
				noscript_tag__parentNode.width = img_tag_width__from_grandparent;
				noscript_tag__parentNode.height = img_tag_height__from_grandparent;
				
				new_img_tag.width = img_tag_width__from_grandparent;
				new_img_tag.setAttribute("height", "auto");
				
			} else {
				noscript_tag__parentNode.setAttribute("width", "100%");
				noscript_tag__parentNode.setAttribute("height", "auto");
				
				new_img_tag.setAttribute("width", "100%");
				new_img_tag.setAttribute("height", "auto");
			}
			
			var j = noscript_tag__parentNode.children.length;

			while(j--){
				try{
					noscript_tag__parentNode.removeChild(noscript_tag__parentNode.lastChild);
			
				} catch(err){
					// alert(err.message);
				}
			}
			
			try{
				noscript_tag__parentNode.appendChild(new_img_tag);
			
			} catch(err){
				// alert(err.message);
			}
		}
	});
	
	// Rework URLs in "img.src + remove "attributes" that may interfere with "src"
	
	var body_tag__img = content.document.getElementsByTagName("body")[0];
	var arr_img_tags = body_tag__img.getElementsByTagName("img");

	var i = arr_img_tags.length;

	while(i--){
		var is_nav_tag = false;		
		var nav_tag = arr_img_tags[i].parentNode;

		while (nav_tag && nav_tag.tagName != 'NAV' && nav_tag.tagName != 'BODY') {
			try{
				nav_tag = nav_tag.parentNode;
				
				if (nav_tag && nav_tag.tagName == "NAV"){
					is_nav_tag = true;
				}
				
			} catch(err){
				// alert(err.message);
			}
		}
		
		if(is_nav_tag){
			continue;
		}
		
		var img_tag_width_height;
		
		var img_tag_width;
		var img_tag_height;
		
		try{
			img_tag_width_height = arr_img_tags[i].getBoundingClientRect();
			
			if(img_tag_width_height){
				img_tag_width = parseInt(img_tag_width_height.width);
				img_tag_height = parseInt(img_tag_width_height.height);
				
				if(!img_tag_height || img_tag_height == 0){
					var img_tag_naturalWidth = parseInt(arr_img_tags[i].naturalWidth);
					var img_tag_naturalHeight = parseInt(arr_img_tags[i].naturalHeight);
					
					if(img_tag_naturalWidth && img_tag_naturalWidth != 0 && img_tag_naturalHeight && img_tag_naturalHeight != 0){
						var img_width_to_height_ratio = img_tag_naturalWidth / img_tag_naturalHeight;
						
						img_tag_height = parseInt(img_tag_width / img_width_to_height_ratio);
					}
				}
			}
			
		} catch(err){
			// alert(err.message);
		}
		
		arr_img_tags[i].style.cssText = "visibility: visible !important; display: run-in !important; z-index: unset; opacity: revert !important;";
		
		var src_pictures_missing = false;
		var str_src_pictures_temp = "";
		
		try{
			var img_src_source = arr_img_tags[i].src;
			
			if(img_src_source){
				var img_src = img_src__add_host_name__add_protocol_for_Google(img_src_source);				
				var str_protocol = img_src.substr(0, 4);
				
				if(str_protocol.toLowerCase() == "http"){
					arr_img_tags[i].src = img_src;
					
				} else{
					src_pictures_missing = true;
				}
			}
			
			var img_srcset = arr_img_tags[i].getAttribute("srcset");
			
			if(img_srcset){
				img_srcset = img_srcset.replace(/,\s*/g, ", ");				
				str_src_pictures_temp = str_src_pictures_temp + img_srcset.trim() + ", ";
				
				arr_img_tags[i].removeAttribute("srcset");
			}
			
			var img_data_srcset = arr_img_tags[i].getAttribute("data-srcset");
				
			if(img_data_srcset){
				img_data_srcset = img_data_srcset.replace(/,\s*/g, ", ");				
				str_src_pictures_temp = str_src_pictures_temp + img_data_srcset.trim() + ", ";
				
				arr_img_tags[i].removeAttribute("data-srcset");
			}
			
			var target = arr_img_tags[i].parentNode;
			
			if(target){
				target.style.cssText = "visibility: visible !important; display: run-in !important; z-index: unset; opacity: revert !important;";
				
				if (target.tagName.toLowerCase() == 'picture') {
					var arr_source_tags = target.getElementsByTagName("source");
					
					if(arr_source_tags){
						var j = arr_source_tags.length;
						
						while(j--){
							var img_srcset_picture = arr_source_tags[j].getAttribute("srcset");
							
							if(img_srcset_picture){
								img_srcset_picture = img_srcset_picture.replace(/,\s*/g, ", ");								
								str_src_pictures_temp = str_src_pictures_temp + img_srcset_picture.trim() + ", ";
								
								arr_source_tags[j].removeAttribute("srcset");
							}
							
							var img_data_srcset_picture = arr_source_tags[j].getAttribute("data-srcset");
								
							if(img_data_srcset_picture){
								img_data_srcset_picture = img_data_srcset_picture.replace(/,\s*/g, ", ");								
								str_src_pictures_temp = str_src_pictures_temp + img_data_srcset_picture.trim() + ", ";
								
								arr_source_tags[j].removeAttribute("data-srcset");
							}
							
							arr_source_tags[j].parentNode.removeChild(arr_source_tags[j]);
						}
					}
				}
			}
			
			if(str_src_pictures_temp.length > 2){
				var str_src = str_src_pictures_temp.substr(0, str_src_pictures_temp.length - 2);				
				var img_src_temp = get_largest_image_src_attribute_for_Google(str_src);				
				var img_src = img_src__add_host_name__add_protocol_for_Google(img_src_temp);
				
				arr_img_tags[i].src = img_src;
				
			} else{
				if(src_pictures_missing){
					var img_data_src = arr_img_tags[i].getAttribute("data-src");
						
					if(img_data_src){
						img_data_src = img_data_src.trim();						
						var img_src = img_src__add_host_name__add_protocol_for_Google(img_data_src);						
						var str_protocol = img_src.substr(0, 4);
						
						if(str_protocol.toLowerCase() == "http"){
							arr_img_tags[i].src = img_src;
						}
					}
				}
			}
			
			arr_img_tags[i].removeAttribute("data-src");
			
			if(img_tag_width && arr_img_tags[i].className != "img__from__amp_img"){
				var page_size = {
					width: content.innerWidth,
					// height: content.innerHeight
				}

				var page_width = page_size.width;
				// var page_height = page_size.height;
				
				if(img_tag_width > page_width){
					var img_with_height = " width: unset !important; height: " + img_tag_height + "px !important; ";					
					var parentNode_minWidth_minHeight = " min-width: unset !important; min-height: " + img_tag_height + "px !important; ";
					
				} else{
					var img_with_height = " width: " + img_tag_width + "px !important; height: " + img_tag_height + "px !important; ";					
					var parentNode_minWidth_minHeight = " min-width: " + img_tag_width + "px !important; min-height: " + img_tag_height + "px !important; ";
				}
				
				if(arr_img_tags[i].parentNode.style.cssText){
					arr_img_tags[i].parentNode.style.cssText = arr_img_tags[i].parentNode.style.cssText + parentNode_minWidth_minHeight;
					
				} else{
					arr_img_tags[i].parentNode.style.cssText = parentNode_minWidth_minHeight;
				}
				
				arr_img_tags[i].style.cssText = arr_img_tags[i].style.cssText + img_with_height;
			}
			
		} catch(err){
			// alert(err.message);
		}
	}
	
	// If the "img" TAG has a "src" with a URL without extension (e.g. PNG, JPG, SVG, etc.) and if that image is of type "svg",
	// then AFTER saving the page followed by opening it in Pale Moon, the "svg" image without extension is no longer displayed.
	// To avoid this problem, we convert this image from "svg" format to "base64" format BEFORE saving the page
	// (see functions "get_image_in_blob_format_and_image_type_for_Google" and "get_base64_image_from_image_blob_for_Google").
	
	content.document.querySelectorAll("img").forEach(image => {
		var img_URL = image.src;
		
		if(img_URL){
			get_image_in_blob_format_and_image_type_for_Google(image, img_URL);
		}
	});
	
	// We make sure that the saved page will have a "favicon"
	
	var tab_favicon = gBrowser.selectedTab.image;

	if(tab_favicon){
		var head_tag = content.document.getElementsByTagName("head")[0];
		
		var head_favicon = content.document.createElement("link");
		head_favicon.type = "image/x-icon";
		head_favicon.rel = "shortcut icon";
		head_favicon.href = tab_favicon;
		
		head_tag.appendChild(head_favicon);
	}
}

function HTML_string_to_DOM_object_for_Google(string){
    var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"].createInstance(Components.interfaces.nsIDOMParser);
    return (parser.parseFromString(string, "text/html"));
}

function img_src__add_host_name__add_protocol_for_Google(img_src__to_check) {
	var img_src = "";
	
	var first_character = img_src__to_check.substr(0, 1);
	
	if(first_character == "/"){
		var second_character = img_src__to_check.substr(1, 1);
		
		if(second_character == "/"){
			img_src = "https:" + img_src__to_check;
			
		} else{
			var page_URL = gBrowser.contentDocument.location.href;

			var ios = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
			var page_URI = ios.newURI(page_URL, null, null);
			var host_name = page_URI.host;
			
			img_src = "https://" + host_name + img_src__to_check;
		}
	}
	
	if(img_src != ""){
		return img_src;
		
	} else{
		return img_src__to_check;
	}
}

function get_largest_image_src_attribute_for_Google(str__src_attributes) {
	var str_src__return = "";
	
	str__src_attributes = str__src_attributes.replace(/%20/g, " ");	
	str__src_attributes = str__src_attributes.trim();
	
	if(str__src_attributes.match(/\s+?\d+w\s*?|\s+?\d+x\s*?/)){
		var arr__src_with_size = [];
		var arr__src_without_size = [];
		
		var regExp_01 = /\s*(?:,|$)\s*/;
		var regExp_02 = /\s+/;
		var regExp_03 = /\s+?\d+w\s*?|\s+?\d+x\s*?/;
		var regExp_04 = /\d+/;
		
		var arr__src_attributes = str__src_attributes.split(regExp_01);
			
		for(var i=0; i<arr__src_attributes.length; i++){
			try{
				var arr___src_size__src = arr__src_attributes[i].split(regExp_02);
				
				if(arr___src_size__src.length == 2){
					var str_src = arr___src_size__src[0].toString();
					
					var str__size_with_w_or_x = arr__src_attributes[i].match(regExp_03);
					
					if(str__size_with_w_or_x){
						var str_size = arr___src_size__src[1].match(regExp_04);						
						var int_size = parseInt(str_size.toString());
						
						var arr_temp = [int_size, str_src];
						arr__src_with_size.push(arr_temp);
						
					} else{
						arr__src_without_size.push(str_src);
					}
				
				} else{
					var str_src = arr___src_size__src[0].toString();					
					arr__src_without_size.push(str_src);
				}
				
			} catch(err){
				// alert(err.message);
			}
		}
		
		if(arr__src_with_size.length > 0){
			var arr__src_with_size__sorted = arr__src_with_size.sort(function(a,b){ return b[0] - a[0]; });

			str_src__return = arr__src_with_size__sorted[0][1];
			
		} else {
			var selected_index = arr__src_without_size.length - 1;
			
			str_src__return = arr__src_without_size[selected_index];
		}
		
	} else{
		var regExp_01 = /\s*(?:,|$)\s*/;		
		var arr__src_attributes = str__src_attributes.split(regExp_01);
		
		if(arr__src_attributes.length > 0){
			var selected_index = arr__src_attributes.length - 1;
			
			str_src__return = arr__src_attributes[selected_index];
			
		} else{
			str_src__return = str__src_attributes;
		}
	}
	
	try{
		str_src__return = decodeURIComponent(str_src__return);
		
	} catch(err){
		// alert(err.message);
	}

	return str_src__return;
}

function get_image_in_blob_format_and_image_type_for_Google(image, img_URL) {
	var xhr = new XMLHttpRequest();

	xhr.onreadystatechange = function() {
		if(this.readyState == 4 && this.status == 200){
			var contentType = xhr.getResponseHeader('Content-Type');
			var img_blob = xhr.response;
			
			if(contentType && img_blob){
				var regEx_type = /image\/svg/i;
				var regEx_extension = /\.svg/i;
				var found_svg_type = contentType.match(regEx_type);
				var found_svg_extension = img_URL.match(regEx_extension);
				
				if(found_svg_type && !found_svg_extension){
					get_base64_image_from_image_blob_for_Google(image, img_blob, contentType);
				}
			}
		}
	};

	xhr.open("GET", img_URL, true);
	xhr.responseType = "blob";
	xhr.send();
}

function get_base64_image_from_image_blob_for_Google(image, img_blob, contentType) {
	try{
		// Convert "blob" to "base64"
		
		var blobToBase64 = function(blob, cb) {
			var reader = new FileReader();
			
			reader.onload = function() {
				var dataUrl = reader.result;
				var base64 = dataUrl.split(',')[1];
				cb(base64);
			}
			
			reader.readAsDataURL(blob);
		};
		
		blobToBase64(img_blob, function(base64) {
			var base64_URL = "data:" + contentType + ";base64," + base64;
			
			image.src = base64_URL;
		});
		
	} catch(err){
		// alert(err.message);
	}
}

///////////////////////////////////////  END - Restore "src" attribute of "img" TAGs  ///////////////////////////////////////

prepare_forum_page_for_download();

function prepare_forum_page_for_download(){
	restore_src_attribute_of_img_TAGs_in_Pale_Moon_Forum_page();
	
	setTimeout(function() {
		var html_tag = content.document.querySelector("html");
		var all_tags = html_tag.getElementsByTagName("*");
		var i = all_tags.length;

		while(i--){
			try{
				var tag_name = all_tags[i].tagName.toLowerCase();
				
				if(tag_name == "img"){
					var img_tag = all_tags[i];
					
					if(img_tag.src){
						var img_src = img_tag.src;
						var img_src_decoded = decodeURIComponent(img_src);

						if(img_src_decoded.includes("download/file.php?id=")){
							var str_i = i.toString();
							
							var link = new URL(img_src_decoded);
							var strippableParams = link.searchParams.keys();
							
							for(var param of strippableParams){
								if(param != "id"){
									link.searchParams.delete(param);
								}
							}
							
							var new_img_src = link.toString();			
							var new_img_src_decoded = decodeURIComponent(new_img_src);
							
							all_tags[i].src = new_img_src_decoded;
							all_tags[i].id = str_i;
							
							var parent_node = all_tags[i].parentNode;
							
							var parent_node__tag_name = parent_node.tagName.toLowerCase();
							
							if(parent_node__tag_name == "a"){
								if(parent_node.href){
									parent_node.style.cursor = "pointer";
									parent_node.setAttribute("class", str_i);
									parent_node.setAttribute("onClick", "window.open(document.getElementById(this.className).src, '_blank'); return false;");
									parent_node.removeAttribute("href");
								}
							}
						}
					}
				}
				
				if(tag_name == "a"){
					var a_tag = all_tags[i];
					
					if(a_tag.href){
						var a_tag_href = a_tag.href;
						var a_tag_href_decoded = decodeURIComponent(a_tag_href);

						if(!a_tag_href_decoded.includes("download/file.php?id=")){
							all_tags[i].setAttribute("target", "_blank");
						}
					}
				}
				
				if(tag_name == "link"){
					var page_URL = gBrowser.contentDocument.location.href;
					var domain_and_protocol = page_URL.split('/').slice(0, 3).join('/');
					
					var name = all_tags[i].getAttribute("rel");
						
					if(name == "stylesheet"){
						var css_href = all_tags[i].href.trim();					
						var first_character = css_href.substr(0, 1);
						
						if(first_character == "/"){
							var second_character = css_href.substr(1, 1);
							
							if(second_character == "/"){
								all_tags[i].href = "https:" + css_href;
								
							} else{
								all_tags[i].href = "" + domain_and_protocol + css_href;
							}
						
						} else{
							all_tags[i].href = "" + css_href;
						}
					}
				}
				
			} catch(err){
				// console.log(err.message);
			}
		}
		
		var css = '@font-face {font-family: "FontAwesome";  font-style: normal;  font-weight: normal;  src: url("fontawesome-webfont.woff2") format("woff2");}';
		var style = content.document.createElement('style');
		style.type = 'text/css';
		style.appendChild(content.document.createTextNode(css));
		
		window.content.document.head.appendChild(style);
		
		var page_URL = gBrowser.contentDocument.location.href;	
		content.document.body.innerHTML = '<table width="100%&lt;tr"><tr><td align="left"><small><a href="' + page_URL + '" target="_blank"><font color="#0000FF"><span style="text-decoration: none">' + page_URL + '</span></font></a></small></td></tr></table><br>' + content.document.body.innerHTML;
		
		setTimeout(function() {
			download_forum_page();
			
		}, 1000);
		
	}, 2000);
}

function select_download_folder(startDir){
	try{
		var nsIFilePicker = Components.interfaces.nsIFilePicker;
		var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
		fp.init(window, "Select Folder", nsIFilePicker.modeGetFolder);

		if(startDir){
			fp.displayDirectory = startDir;
		}

		var rv = fp.show();

		if(rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace){
			return fp.file;
		}

		return undefined;
		
    } catch(err){
		// console.log(err.message);
	}
}

function download_forum_page(){
	var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties);
	var startDir = dirSvc.get("Desk", Components.interfaces.nsILocalFile);		// Desktop

	var obj_download_folder = select_download_folder(startDir);
	var download_folder_path = obj_download_folder.path;

	var path_HTML_file = "file:///" + download_folder_path.replace(/\\/g, "/") + "/_index.html";

	var localFolder = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
	var persist = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Components.interfaces.nsIWebBrowserPersist);
	persist.persistFlags |= persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
	persist.persistFlags |= persist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;

	localFolder.initWithPath(download_folder_path);
	var localFile = localFolder.clone();
	localFile.append("_index.html");

	persist.progressListener = {
		onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
			// The next "if" is executed AFTER all files have been saved to Hard-Disk
			
			if (persist.currentState == persist.PERSIST_STATE_FINISHED) {
				complete_the_process_after_saving_the_page_on_Hard_Disk(path_HTML_file, download_folder_path);
			}
		},
		
		onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {
            // The following IF is executed BEFORE all files are saved to Hard-Disk
			
			// "aCurTotalProgress" and "aMaxTotalProgress" are useful ONLY if we want to create a "Progress Bar"
			// calculating the percentage of "aCurTotalProgress" from "aMaxTotalProgress"
			
			if (aCurTotalProgress == aMaxTotalProgress){ }
		}
	}

	persist.saveDocument(gBrowser.contentDocument, localFile, localFolder, null, null, null);
}

function complete_the_process_after_saving_the_page_on_Hard_Disk(path_HTML_file, download_folder_path) {
	var final_position = gBrowser.tabContainer.selectedIndex + 1;
	var new_tab = gBrowser.addTab(path_HTML_file);
	gBrowser.moveTabTo(new_tab, final_position);
	gBrowser.selectedTab = new_tab;
	
	setTimeout(function() {
		var a_tag = content.document.createElement("a");
		a_tag.setAttribute("target", "_top");
		var file_name = "fontawesome-webfont.woff2";
		a_tag.setAttribute('download', file_name);
		a_tag.href = "https://forum.palemoon.org/assets/fonts/fontawesome-webfont.woff2";
		a_tag.innerHTML = 'fontawesome-webfont.woff2';
		a_tag.style.display = 'none';
		content.document.body.appendChild(a_tag);
		a_tag.click();
		
		var download_folder_path__with_back_slashes = download_folder_path.replace(/\\/g, "\\\\\\\\") + "\\\\\\\\";
		var saved_HTML_file_name = "_index.html";
		var saved_HTML_file_path__with_back_slashes = download_folder_path__with_back_slashes + saved_HTML_file_name;
		var webfont_file_path = saved_HTML_file_path__with_back_slashes.replace(/_index.html/, "fontawesome-webfont.woff2");
		
		var index = 0;
		
		(function asyncLoop(){
			index = index + 1;
			
			setTimeout(function() {
				try{
					// new file object
					var obj_TargetFile = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);

					// set file with path
					obj_TargetFile.initWithPath(webfont_file_path);
					
					// if file exist
					if(obj_TargetFile.exists()) {
						gBrowser.contentDocument.location.reload(true);
						
						return;
					}
					
					if(index <= 60){
						asyncLoop();
					}
					
				} catch(err){
					// console.log(err.message);
				}
				
			}, 1000);
			
		})();
		
	}, 2000)
}


Custom Buttons Enhanced 0.0.6

https://forum.palemoon.org/viewtopic.php?f=71&t=32626

With this Custom Button, you can save the current Pale Moon Forum Page to the folder you selected after you clicked the button.

IMPORTANT !
When the pop-up titled "Opening fontawesome-webfont.woff2" appears, select the "Save File" option and save this file in the same folder you selected earlier.
The "fontawesome-webfont.woff2" file can only be saved with the help of an "A" TAG inserted into the page because the other methods tested get stuck in the "Anubis" verification.
The file "fontawesome-webfont.woff2" loads on page the icons for various categories ("Quick links", "FAQ", "Rules", ETC.)

IMPORTANT !
After saving a page with this Custom Button, reload the saved page and check if all images have been saved (scroll from top to bottom). If there are images that are not displayed on page (this happens in very rare cases), reload the forum page and repeat the procedure from the beginning - for that page (see below).

Screenshots:

1.Page saved with "File / Save Page As" from the Pale Moon menu:
01.png
2. Page saved with "ScrapBook Plus" add-on - which does not save image files:
02.png
03.png
3. Saving the page with a Custom Button named "Download Pale Moon Forum Page":
04.png
05.png
06.png
07.png
08.png
09.png
You do not have the required permissions to view the files attached to this post.