From 95b0ce5e3a64d966924dd701d1e336402d586b25 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Oct 2024 08:35:43 -0400 Subject: [PATCH] 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]) --- assets/resources/scriptlets.js | 64 ++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index f674c714d..2b188ebef 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -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); + }); +} + /******************************************************************************/