1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-07-05 11:37:01 +02:00

Add ability to execute aeld scriptlet at a later time

As per discussion with filter list maintainers.

THis requires to use JSON notation for parameter passing:

- "runAt":  "end" = execute scriptlet at `DOMContentLoaded` event
- "runAt": "idle" = execute scriptlet at `load` event
This commit is contained in:
Raymond Hill 2023-04-27 12:52:17 -04:00
parent 7cba521bc4
commit 3c12173dfe
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2

View File

@ -117,6 +117,35 @@ function shouldLog(details) {
return scriptletGlobals.has('canDebug') && details.log;
}
/******************************************************************************/
builtinScriptlets.push({
name: 'run-at.fn',
fn: runAt,
});
function runAt(fn, when) {
const intFromReadyState = state => {
return ({
loading: 1,
interactive: 2,
end: 2,
complete: 3,
idle: 3,
})[`${state}`] || 0;
};
const runAt = intFromReadyState(when);
if ( intFromReadyState(document.readyState) >= runAt ) {
fn(); return;
}
const options = { capture: true };
const onStateChange = ( ) => {
if ( intFromReadyState(document.readyState) < runAt ) { return; }
fn();
document.removeEventListener('readystatechange', onStateChange, options);
};
document.addEventListener('readystatechange', onStateChange, options);
}
/*******************************************************************************
Injectable scriptlets
@ -432,6 +461,7 @@ builtinScriptlets.push({
fn: addEventListenerDefuser,
dependencies: [
'pattern-to-regex.fn',
'run-at.fn',
'safe-self.fn',
'should-debug.fn',
'should-log.fn',
@ -453,29 +483,45 @@ function addEventListenerDefuser(
const rePattern = patternToRegex(pattern);
const log = shouldLog(details);
const debug = shouldDebug(details);
const proto = self.EventTarget.prototype;
proto.addEventListener = new Proxy(proto.addEventListener, {
apply: function(target, thisArg, args) {
let type, handler;
try {
type = String(args[0]);
handler = String(args[1]);
} catch(ex) {
const trapEddEventListeners = ( ) => {
const eventListenerHandler = {
apply: function(target, thisArg, args) {
let type, handler;
try {
type = String(args[0]);
handler = String(args[1]);
} catch(ex) {
}
const matchesType = safe.RegExp_test.call(reType, type);
const matchesHandler = safe.RegExp_test.call(rePattern, handler);
const matchesEither = matchesType || matchesHandler;
const matchesBoth = matchesType && matchesHandler;
if ( log === 1 && matchesBoth || log === 2 && matchesEither || log === 3 ) {
safe.uboLog(`addEventListener('${type}', ${handler})`);
}
if ( debug === 1 && matchesBoth || debug === 2 && matchesEither ) {
debugger; // jshint ignore:line
}
if ( matchesBoth ) { return; }
return Reflect.apply(target, thisArg, args);
}
const matchesType = safe.RegExp_test.call(reType, type);
const matchesHandler = safe.RegExp_test.call(rePattern, handler);
const matchesEither = matchesType || matchesHandler;
const matchesBoth = matchesType && matchesHandler;
if ( log === 1 && matchesBoth || log === 2 && matchesEither || log === 3 ) {
safe.uboLog(`addEventListener('${type}', ${handler})`);
}
if ( debug === 1 && matchesBoth || debug === 2 && matchesEither ) {
debugger; // jshint ignore:line
}
if ( matchesBoth ) { return; }
return Reflect.apply(target, thisArg, args);
}
});
};
self.EventTarget.prototype.addEventListener = new Proxy(
self.EventTarget.prototype.addEventListener,
eventListenerHandler
);
self.document.addEventListener = new Proxy(
self.document.addEventListener,
eventListenerHandler
);
self.addEventListener = new Proxy(
self.addEventListener,
eventListenerHandler
);
};
runAt(( ) => {
trapEddEventListeners();
}, details.runAt);
}
/******************************************************************************/
@ -979,6 +1025,9 @@ builtinScriptlets.push({
name: 'set-constant.js',
aliases: [ 'set.js' ],
fn: setConstant,
dependencies: [
'run-at.fn',
],
});
function setConstant(
arg1 = '',
@ -1144,22 +1193,9 @@ function setConstant(
};
trapChain(window, chain);
}
const runAt = details.runAt;
if ( runAt === 0 ) {
setConstant(chain, cValue); return;
}
const docReadyState = ( ) => {
return ({ loading: 1, interactive: 2, complete: 3, })[document.readyState] || 0;
};
if ( docReadyState() >= runAt ) {
setConstant(chain, cValue); return;
}
const onReadyStateChange = ( ) => {
if ( docReadyState() < runAt ) { return; }
runAt(( ) => {
setConstant(chain, cValue);
document.removeEventListener('readystatechange', onReadyStateChange);
};
document.addEventListener('readystatechange', onReadyStateChange);
}, details.runAt);
}
/******************************************************************************/