Fixed: HTML filter (#277)
This commit is contained in:
parent
9bca268a98
commit
ad00d4c3a8
|
@ -34,90 +34,91 @@ manipulateDOM._removeCrossOriginAndIntegrityAttr = function (details) {
|
||||||
console.warn('[ LocalCDN ] browser.webRequest.filterResponseData not supported by your browser.');
|
console.warn('[ LocalCDN ] browser.webRequest.filterResponseData not supported by your browser.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (details.statusCode === 200) {
|
||||||
|
let initiatorDomain, listedToManipulateDOM, negateHtmlFilter, filtering, header;
|
||||||
|
|
||||||
let initiatorDomain, listedToManipulateDOM, negateHtmlFilter, filtering, header;
|
initiatorDomain = helpers.extractDomainFromUrl(details.url, true) || Address.EXAMPLE;
|
||||||
|
listedToManipulateDOM = stateManager._domainIsListed(initiatorDomain, 'manipulate-dom');
|
||||||
|
negateHtmlFilter = stateManager.getInvertOption;
|
||||||
|
|
||||||
initiatorDomain = helpers.extractDomainFromUrl(details.url, true) || Address.EXAMPLE;
|
if ((negateHtmlFilter || listedToManipulateDOM) && !(negateHtmlFilter && listedToManipulateDOM)) {
|
||||||
listedToManipulateDOM = stateManager._domainIsListed(initiatorDomain, 'manipulate-dom');
|
filtering = true;
|
||||||
negateHtmlFilter = stateManager.getInvertOption;
|
} else {
|
||||||
|
filtering = false;
|
||||||
if ((negateHtmlFilter || listedToManipulateDOM) && !(negateHtmlFilter && listedToManipulateDOM)) {
|
|
||||||
filtering = true;
|
|
||||||
} else {
|
|
||||||
filtering = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// by Jaap (https://gitlab.com/Jaaap)
|
|
||||||
header = details.responseHeaders.find((h) => h.name.toLowerCase() === 'content-type');
|
|
||||||
|
|
||||||
if (header && filtering) {
|
|
||||||
|
|
||||||
let mimeType, isAllowlisted;
|
|
||||||
|
|
||||||
mimeType = header.value.replace(/;.*/, '').toLowerCase();
|
|
||||||
isAllowlisted = stateManager._domainIsListed(initiatorDomain);
|
|
||||||
|
|
||||||
if (!isAllowlisted && mimeType === 'text/html') {
|
|
||||||
|
|
||||||
let asciiDecoder, decoder, encoder, charset, isFirstData, filter, data;
|
|
||||||
|
|
||||||
charset = (/charset\s*=/).test(header.value) && header.value.replace(/^.*?charset\s*=\s*/, '').replace(/["']?/g, '');
|
|
||||||
|
|
||||||
// Check if charset is supported by TextDecoder()
|
|
||||||
if (/charset\s*=/.test(header.value) && !EncodingTypes[charset.toString().toLowerCase()]) {
|
|
||||||
console.error(`[ LocalCDN ] Unsupported charset: ${charset}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
asciiDecoder = new TextDecoder('ASCII');
|
|
||||||
encoder = new TextEncoder();
|
|
||||||
isFirstData = true;
|
|
||||||
filter = browser.webRequest.filterResponseData(details.requestId);
|
|
||||||
data = [];
|
|
||||||
|
|
||||||
header.value = 'text/html; charset=UTF-8';
|
|
||||||
|
|
||||||
// NOTE: should work if 'script' string is divided into two chunks
|
|
||||||
filter.ondata = (evt) => {
|
|
||||||
if (isFirstData) {
|
|
||||||
if (!charset) {
|
|
||||||
// content-type has no charset declared
|
|
||||||
let htmlHead, charsetMatch;
|
|
||||||
|
|
||||||
htmlHead = asciiDecoder.decode(evt.data, {'stream': false});
|
|
||||||
// eslint-disable-next-line no-useless-escape
|
|
||||||
charsetMatch = htmlHead.match(/<meta.*charset=["']?([^>"'\/]+)["'].*[>\/]/i);
|
|
||||||
|
|
||||||
charset = charsetMatch ? charsetMatch[1] : 'UTF-8';
|
|
||||||
}
|
|
||||||
decoder = new TextDecoder(charset);
|
|
||||||
}
|
|
||||||
isFirstData = false;
|
|
||||||
|
|
||||||
data.push(evt.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
filter.onstop = () => {
|
|
||||||
let str = '';
|
|
||||||
for (let buffer of data) {
|
|
||||||
str += decoder.decode(buffer, {'stream': true});
|
|
||||||
}
|
|
||||||
str += decoder.decode(); // end-of-stream
|
|
||||||
|
|
||||||
// remove crossorigin and integrity attributes
|
|
||||||
str = str.replace(/<(link|script)[^>]+>/ig, (m) => {
|
|
||||||
// eslint-disable-next-line no-use-before-define
|
|
||||||
if (cdnDomainsRE.test(m)) {
|
|
||||||
return m.replace(/\s+(integrity|crossorigin)(="[^"]*"|='[^']*'|=[^"'`=>\s]+|)/ig, '');
|
|
||||||
}
|
|
||||||
return m;
|
|
||||||
});
|
|
||||||
filter.write(encoder.encode(str));
|
|
||||||
filter.close();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return {'responseHeaders': details.responseHeaders};
|
|
||||||
|
|
||||||
|
// by Jaap (https://gitlab.com/Jaaap)
|
||||||
|
header = details.responseHeaders.find((h) => h.name.toLowerCase() === 'content-type');
|
||||||
|
|
||||||
|
if (header && filtering) {
|
||||||
|
|
||||||
|
let mimeType, isAllowlisted;
|
||||||
|
|
||||||
|
mimeType = header.value.replace(/;.*/, '').toLowerCase();
|
||||||
|
isAllowlisted = stateManager._domainIsListed(initiatorDomain);
|
||||||
|
|
||||||
|
if (!isAllowlisted && mimeType === 'text/html') {
|
||||||
|
|
||||||
|
let asciiDecoder, decoder, encoder, charset, isFirstData, filter, data;
|
||||||
|
|
||||||
|
charset = (/charset\s*=/).test(header.value) && header.value.replace(/^.*?charset\s*=\s*/, '').replace(/["']?/g, '');
|
||||||
|
|
||||||
|
// Check if charset is supported by TextDecoder()
|
||||||
|
if (/charset\s*=/.test(header.value) && !EncodingTypes[charset.toString().toLowerCase()]) {
|
||||||
|
console.error(`[ LocalCDN ] Unsupported charset: ${charset}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
asciiDecoder = new TextDecoder('ASCII');
|
||||||
|
encoder = new TextEncoder();
|
||||||
|
isFirstData = true;
|
||||||
|
filter = browser.webRequest.filterResponseData(details.requestId);
|
||||||
|
data = [];
|
||||||
|
|
||||||
|
header.value = 'text/html; charset=UTF-8';
|
||||||
|
|
||||||
|
// NOTE: should work if 'script' string is divided into two chunks
|
||||||
|
filter.ondata = (evt) => {
|
||||||
|
if (isFirstData) {
|
||||||
|
if (!charset) {
|
||||||
|
// content-type has no charset declared
|
||||||
|
let htmlHead, charsetMatch;
|
||||||
|
|
||||||
|
htmlHead = asciiDecoder.decode(evt.data, {'stream': false});
|
||||||
|
// eslint-disable-next-line no-useless-escape
|
||||||
|
charsetMatch = htmlHead.match(/<meta.*charset=["']?([^>"'\/]+)["'].*[>\/]/i);
|
||||||
|
|
||||||
|
charset = charsetMatch ? charsetMatch[1] : 'UTF-8';
|
||||||
|
}
|
||||||
|
decoder = new TextDecoder(charset);
|
||||||
|
}
|
||||||
|
isFirstData = false;
|
||||||
|
|
||||||
|
data.push(evt.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
filter.onstop = () => {
|
||||||
|
let str = '';
|
||||||
|
for (let buffer of data) {
|
||||||
|
str += decoder.decode(buffer, {'stream': true});
|
||||||
|
}
|
||||||
|
str += decoder.decode(); // end-of-stream
|
||||||
|
|
||||||
|
// remove crossorigin and integrity attributes
|
||||||
|
str = str.replace(/<(link|script)[^>]+>/ig, (m) => {
|
||||||
|
// eslint-disable-next-line no-use-before-define
|
||||||
|
if (cdnDomainsRE.test(m)) {
|
||||||
|
return m.replace(/\s+(integrity|crossorigin)(="[^"]*"|='[^']*'|=[^"'`=>\s]+|)/ig, '');
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
filter.write(encoder.encode(str));
|
||||||
|
filter.close();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {'responseHeaders': details.responseHeaders};
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
New in LocalCDN:
|
New in LocalCDN:
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li></li>
|
<li>Fixed: Check status code in HTML filter (<a href="https://codeberg.org/nobody/LocalCDN/issues/277">#277</a>)</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div id="generator-section">
|
<div id="generator-section">
|
||||||
<div class="topic-label">
|
<div class="topic-label">
|
||||||
|
|
Loading…
Reference in New Issue