From b22b3d729b4660fcc481fe9f7b4fe367a1bdd714 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 14 Feb 2024 08:23:16 -0500 Subject: [PATCH] Improve `prevent-addEventListener` scriptlet Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/3061#issuecomment-1899042062 --- assets/resources/scriptlets.js | 47 ++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 121e9349eae17..0bc532607f8d1 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -1587,17 +1587,35 @@ function addEventListenerDefuser( pattern = '' ) { const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('prevent-addEventListener', type, pattern); const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); + const logPrefix = safe.makeLogPrefix('prevent-addEventListener', type, pattern); const reType = safe.patternToRegex(type, undefined, true); const rePattern = safe.patternToRegex(pattern); const debug = shouldDebug(extraArgs); const targetSelector = extraArgs.elements || undefined; - const shouldPrevent = (thisArg, type, handler) => { - if ( targetSelector !== undefined ) { - const elems = Array.from(document.querySelectorAll(targetSelector)); - if ( elems.includes(thisArg) === false ) { return false; } + const elementMatches = elem => { + if ( elem && elem.matches && elem.matches(targetSelector) ) { return true; } + const elems = Array.from(document.querySelectorAll(targetSelector)); + return elems.includes(elem); + }; + const elementDetails = elem => { + if ( elem instanceof Window ) { return 'window'; } + if ( elem instanceof Document ) { return 'document'; } + if ( elem instanceof Element === false ) { return '?'; } + const parts = []; + if ( elem.id !== '' ) { parts.push(`#${CSS.escape(elem.id)}`); } + for ( let i = 0; i < elem.classList.length; i++ ) { + parts.push(`.${CSS.escape(elem.classList.item(i))}`); + } + for ( let i = 0; i < elem.attributes.length; i++ ) { + const attr = elem.attributes.item(i); + if ( attr.name === 'id' ) { continue; } + if ( attr.name === 'class' ) { continue; } + parts.push(`[${CSS.escape(attr.name)}="${attr.value}"]`); } + return parts.join(''); + }; + const shouldPrevent = (thisArg, type, handler) => { const matchesType = safe.RegExp_test.call(reType, type); const matchesHandler = safe.RegExp_test.call(rePattern, handler); const matchesEither = matchesType || matchesHandler; @@ -1605,6 +1623,9 @@ function addEventListenerDefuser( if ( debug === 1 && matchesBoth || debug === 2 && matchesEither ) { debugger; // jshint ignore:line } + if ( matchesBoth && targetSelector !== undefined ) { + if ( elementMatches(thisArg) === false ) { return false; } + } return matchesBoth; }; const trapEddEventListeners = ( ) => { @@ -1613,15 +1634,21 @@ function addEventListenerDefuser( let t, h; try { t = String(args[0]); - h = args[1] instanceof Function - ? String(safe.Function_toString(args[1])) - : String(args[1]); + if ( typeof args[1] === 'function' ) { + h = String(safe.Function_toString(args[1])); + } else if ( typeof args[1] === 'object' && args[1] !== null ) { + if ( typeof args[1].handleEvent === 'function' ) { + h = String(safe.Function_toString(args[1].handleEvent)); + } + } else { + h = String(args[1]); + } } catch(ex) { } if ( type === '' && pattern === '' ) { - safe.uboLog(logPrefix, `Called: ${t}\n${h}`); + safe.uboLog(logPrefix, `Called: ${t}\n${h}\n${elementDetails(thisArg)}`); } else if ( shouldPrevent(thisArg, t, h) ) { - return safe.uboLog(logPrefix, `Prevented: ${t}\n${h}`); + return safe.uboLog(logPrefix, `Prevented: ${t}\n${h}\n${elementDetails(thisArg)}`); } return Reflect.apply(target, thisArg, args); },