1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-11-16 23:42:39 +01:00

Improve prevent-xhr scriptlet

As per filter list maintainers feedback.
This commit is contained in:
Raymond Hill 2024-08-26 14:28:16 -04:00
parent 26b2ab8bb5
commit 3a249f395c
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2

View File

@ -2695,6 +2695,13 @@ function noXhrIf(
'content-type': '', 'content-type': '',
'content-length': '', 'content-length': '',
}; };
const safeDispatchEvent = (xhr, type) => {
try {
xhr.dispatchEvent(new Event(type));
} catch(_) {
}
};
const XHRBefore = XMLHttpRequest.prototype;
self.XMLHttpRequest = class extends self.XMLHttpRequest { self.XMLHttpRequest = class extends self.XMLHttpRequest {
open(method, url, ...args) { open(method, url, ...args) {
xhrInstances.delete(this); xhrInstances.delete(this);
@ -2721,27 +2728,26 @@ function noXhrIf(
let promise = Promise.resolve({ let promise = Promise.resolve({
xhr: this, xhr: this,
directive, directive,
props: { response: {
readyState: { value: 4 },
response: { value: '' }, response: { value: '' },
responseText: { value: '' }, responseText: { value: '' },
responseXML: { value: null }, responseXML: { value: null },
responseURL: { value: haystack.url }, responseURL: { value: haystack.url },
status: { value: 200 }, }
statusText: { value: 'OK' },
},
}); });
switch ( this.responseType ) { switch ( this.responseType ) {
case 'arraybuffer': case 'arraybuffer':
promise = promise.then(details => { promise = promise.then(details => {
details.props.response.value = new ArrayBuffer(0); const response = details.response;
response.response.value = new ArrayBuffer(0);
return details; return details;
}); });
haystack.headers['content-type'] = 'application/octet-stream'; haystack.headers['content-type'] = 'application/octet-stream';
break; break;
case 'blob': case 'blob':
promise = promise.then(details => { promise = promise.then(details => {
details.props.response.value = new Blob([]); const response = details.response;
response.response.value = new Blob([]);
return details; return details;
}); });
haystack.headers['content-type'] = 'application/octet-stream'; haystack.headers['content-type'] = 'application/octet-stream';
@ -2750,8 +2756,9 @@ function noXhrIf(
promise = promise.then(details => { promise = promise.then(details => {
const parser = new DOMParser(); const parser = new DOMParser();
const doc = parser.parseFromString('', 'text/html'); const doc = parser.parseFromString('', 'text/html');
details.props.response.value = doc; const response = details.response;
details.props.responseXML.value = doc; response.response.value = doc;
response.responseXML.value = doc;
return details; return details;
}); });
haystack.headers['content-type'] = 'text/html'; haystack.headers['content-type'] = 'text/html';
@ -2759,8 +2766,9 @@ function noXhrIf(
} }
case 'json': case 'json':
promise = promise.then(details => { promise = promise.then(details => {
details.props.response.value = {}; const response = details.response;
details.props.responseText.value = '{}'; response.response.value = {};
response.responseText.value = '{}';
return details; return details;
}); });
haystack.headers['content-type'] = 'application/json'; haystack.headers['content-type'] = 'application/json';
@ -2769,8 +2777,9 @@ function noXhrIf(
if ( directive === '' ) { break; } if ( directive === '' ) { break; }
promise = promise.then(details => { promise = promise.then(details => {
return generateContentFn(details.directive).then(text => { return generateContentFn(details.directive).then(text => {
details.props.response.value = text; const response = details.response;
details.props.responseText.value = text; response.response.value = text;
response.responseText.value = text;
return details; return details;
}); });
}); });
@ -2778,11 +2787,35 @@ function noXhrIf(
break; break;
} }
promise.then(details => { promise.then(details => {
haystack.headers['content-length'] = `${details.props.response.value}`.length; Object.defineProperties(details.xhr, {
Object.defineProperties(details.xhr, details.props); readyState: { value: 1, configurable: true },
details.xhr.dispatchEvent(new Event('readystatechange')); });
details.xhr.dispatchEvent(new Event('load')); safeDispatchEvent(details.xhr, 'readystatechange');
details.xhr.dispatchEvent(new Event('loadend')); return details;
}).then(details => {
const response = details.response;
haystack.headers['content-length'] = `${response.response.value}`.length;
Object.defineProperties(details.xhr, {
readyState: { value: 2, configurable: true },
status: { value: 200 },
statusText: { value: 'OK' },
});
safeDispatchEvent(details.xhr, 'readystatechange');
return details;
}).then(details => {
Object.defineProperties(details.xhr, {
readyState: { value: 3, configurable: true },
});
Object.defineProperties(details.xhr, details.response);
safeDispatchEvent(details.xhr, 'readystatechange');
return details;
}).then(details => {
Object.defineProperties(details.xhr, {
readyState: { value: 4 },
});
safeDispatchEvent(details.xhr, 'readystatechange');
safeDispatchEvent(details.xhr, 'load');
safeDispatchEvent(details.xhr, 'loadend');
safe.uboLog(logPrefix, `Prevented with response:\n${details.xhr.response}`); safe.uboLog(logPrefix, `Prevented with response:\n${details.xhr.response}`);
}); });
} }
@ -2809,6 +2842,18 @@ function noXhrIf(
return out.join('\r\n'); return out.join('\r\n');
} }
}; };
self.XMLHttpRequest.prototype.open.toString = function() {
return XHRBefore.open.toString();
};
self.XMLHttpRequest.prototype.send.toString = function() {
return XHRBefore.send.toString();
};
self.XMLHttpRequest.prototype.getResponseHeader.toString = function() {
return XHRBefore.getResponseHeader.toString();
};
self.XMLHttpRequest.prototype.getAllResponseHeaders.toString = function() {
return XHRBefore.getAllResponseHeaders.toString();
};
} }
/******************************************************************************/ /******************************************************************************/