Add `trusted-override-element-method` scriptlet

@description
Override the behavior of a method on matching elements.

@param methodPath
The method which calls must be intercepted.

@param [selector]
A CSS selector which the target element must match. If not specified,
the override will occur for all elements.

@param [disposition]
How the override should be handled. If not specified, the overridden call
will be equivalent to an empty function. If set to `throw`, an exception
will be thrown. Any other value will be validated and returned as a
supported safe constant.

@example
..##+js(trusted-override-element-method, HTMLAnchorElement.prototype.click, a[target="_blank"][style])
This commit is contained in:
Raymond Hill 2024-10-05 08:35:43 -04:00
parent a0a33eb9b9
commit 95b0ce5e3a
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
1 changed files with 64 additions and 0 deletions

View File

@ -5218,4 +5218,68 @@ function trustedPreventDomBypass(
});
}
/**
*
* @trustedScriptlet trusted-override-element-method
*
* @description
* Override the behavior of a method on matching elements.
*
* @param methodPath
* The method which calls must be intercepted.
*
* @param [selector]
* A CSS selector which the target element must match. If not specified,
* the override will occur for all elements.
*
* @param [disposition]
* How the override should be handled. If not specified, the overridden call
* will be equivalent to an empty function. If set to `throw`, an exception
* will be thrown. Any other value will be validated and returned as a
* supported safe constant.
*
* @example
* ##+js(trusted-override-element-method, HTMLAnchorElement.prototype.click, a[target="_blank"][style])
*
* */
builtinScriptlets.push({
name: 'trusted-override-element-method.js',
requiresTrust: true,
fn: trustedOverrideElementMethod,
dependencies: [
'proxy-apply.fn',
'safe-self.fn',
'validate-constant.fn',
],
});
function trustedOverrideElementMethod(
methodPath = '',
selector = '',
disposition = ''
) {
if ( methodPath === '' ) { return; }
const safe = safeSelf();
const logPrefix = safe.makeLogPrefix('trusted-override-element-method', methodPath, selector, disposition);
proxyApplyFn(methodPath, function(context) {
let override = selector === '';
if ( override === false ) {
const { thisArg } = context;
try {
override = thisArg.closest(selector) === thisArg;
} catch(_) {
}
}
if ( override === false ) {
return context.reflect();
}
safe.uboLog(logPrefix, 'Overridden');
if ( disposition === '' ) { return; }
if ( disposition === 'throw' ) {
throw new ReferenceError();
}
return validateConstantFn(false, disposition);
});
}
/******************************************************************************/