Scriptlets
Scriptlets copied to clipboard
Improve 'prevent-xhr' — add missed events
Related to https://github.com/AdguardTeam/AdguardFilters/issues/175034 and https://github.com/AdguardTeam/AdguardFilters/issues/174876
These websites use onreadystatechange to check if 4 events were fired and #%#//scriptlet('prevent-xhr', 'pagead2.googlesyndication.com') doesn't work because currently only 2 events are invoked.
Steps to reproduce:
- Add this rule:
example.org#%#//scriptlet('prevent-xhr', 'pagead2.googlesyndication.com')
- Go to -
https://example.org/ - In browser console run:
(() => {
const checkDection = (detected) => {
if (detected) {
alert('AdBlocker detected');
return;
}
const allEventsPassed = xhrEvents.every((state) => state);
if (!allEventsPassed) {
alert('AdBlocker detected');
return;
}
// No AdBlocker detected, do something
console.log('No AdBlocker detected');
};
const url = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
const xhrEvents = [false, false, false, false];
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
xhrEvents[xhr.readyState - 1] = true;
if (xhr.readyState === 4) {
checkDection();
}
};
try {
xhr.open("GET", url, true);
xhr.send();
} catch (ex) {
checkDection(true);
}
})();
Probably changing this part: https://github.com/AdguardTeam/Scriptlets/blob/804c53527008e62ffcd296e3f36543c472b96fe5/src/scriptlets/prevent-xhr.js#L184-L223
to something like:
Code:
forgedRequest.addEventListener('readystatechange', () => {
Object.defineProperty(thisArg, 'readyState', { value: forgedRequest.readyState, writable: true }); // it seems that - thisArg.readyState = forgedRequest.readyState - doesn't work, but maybe I didn't check it correctly
const stateEvent = new Event("readystatechange");
switch (forgedRequest.readyState) {
case 1:
thisArg.dispatchEvent(stateEvent);
const loadStartEvent = new Event("loadstart");
thisArg.dispatchEvent(loadStartEvent);
break;
case 2:
thisArg.dispatchEvent(stateEvent);
const progressEvent = new Event("progress");
thisArg.dispatchEvent(progressEvent);
break;
case 3:
thisArg.dispatchEvent(stateEvent);
const loadEvent = new Event("load");
thisArg.dispatchEvent(loadEvent);
break;
case 4:
const {
readyState,
responseURL,
responseXML,
statusText
} = forgedRequest;
// Mock response object
Object.defineProperties(thisArg, {
// original values
readyState: {
value: readyState,
writable: false
},
statusText: {
value: statusText,
writable: false
},
// If the request is blocked, responseURL is an empty string
responseURL: {
value: responseURL || thisArg.xhrData.url,
writable: false
},
responseXML: {
value: responseXML,
writable: false
},
// modified values
status: {
value: 200,
writable: false
},
response: {
value: modifiedResponse,
writable: false
},
responseText: {
value: modifiedResponseText,
writable: false
}
});
// Mock events
setTimeout(() => {
thisArg.dispatchEvent(stateEvent);
const loadEndEvent = new Event('loadend');
thisArg.dispatchEvent(loadEndEvent);
}, 1);
}
hit(source);
});
should fixes it.