From 99ce027fd70241051a756ad635e7d1d71a25eca1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 22 May 2023 10:26:09 -0400 Subject: [PATCH] Review default behavior of new sed.js scriptlet Related commit: - https://github.com/gorhill/uBlock/commit/41876336db48292de06707adfa5e97dab74297d2 The `tryCount` parameter has been removed. The new default behavior of the sed.js scriptlet is to bail out when the document itself has been fully loaded, i.e. when DOMContentLoaded event is fired. Two new parameters have been added to override the default quit out behavior: `stay, 1` Use to force the scriptlet to stay at work forever. `quitAfter, ms` This tells the scriptlet to quit `ms` milliseconds after the page has been loaded, i.e. after the DOMContentLoaded event has been fired. The mutation observer of the sed.js scriptlet can be a significant overhead for pages with dynamically updated DOM, and in most cases the scriptlet is useful only for DOM changes occurring before the DOMContentLoaded event, so the default is to quit out when that event is received ("quit out" means discarding the mutation observer and having the scriptlet garbage-collected by the JS engine). --- assets/resources/scriptlets.js | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 4ea602c88..8215a29e5 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -2220,6 +2220,7 @@ builtinScriptlets.push({ fn: sed, dependencies: [ 'pattern-to-regex.fn', + 'run-at.fn', 'safe-self.fn', ], }); @@ -2237,10 +2238,19 @@ function sed( }, []) ); const shouldLog = scriptletGlobals.has('canDebug') && extraArgs.get('log') || 0; + const shouldStay = extraArgs.get('stay') || false; const reCondition = patternToRegex(extraArgs.get('condition') || '', 'gms'); - let sedCount = extraArgs.has('sedCount') ? parseInt(extraArgs.get('sedCount')) : 0; - let tryCount = extraArgs.has('tryCount') ? parseInt(extraArgs.get('tryCount')) : 0; const safe = safeSelf(); + const stop = (takeRecord = true) => { + if ( takeRecord ) { + handler(observer.takeRecords()); + } + observer.disconnect(); + if ( shouldLog !== 0 ) { + safe.uboLog(`sed.js: quitting "${pattern}" => "${replacement}"`); + } + }; + let sedCount = extraArgs.has('sedCount') ? parseInt(extraArgs.get('sedCount')) : 0; const handler = mutations => { for ( const mutation of mutations ) { for ( const node of mutation.addedNodes ) { @@ -2253,22 +2263,25 @@ function sed( if ( shouldLog !== 0 ) { safe.uboLog('sed.js after:\n', after); } node.textContent = after; if ( sedCount !== 0 && (sedCount -= 1) === 0 ) { - observer.disconnect(); - if ( shouldLog !== 0 ) { safe.uboLog('sed.js: quitting'); } - return; + return stop(false); } } } - if ( tryCount !== 0 && (tryCount -= 1) === 0 ) { - observer.disconnect(); - if ( shouldLog !== 0 ) { safe.uboLog('sed.js: quitting'); } - } }; const observer = new MutationObserver(handler); observer.observe(document.documentElement, { childList: true, subtree: true, }); + if ( shouldStay ) { return; } + runAt(( ) => { + const quitAfter = parseInt(extraArgs.get('quitAfter')) || 0; + if ( quitAfter !== 0 ) { + setTimeout(( ) => { stop(); }, quitAfter); + } else { + stop(); + } + }, 'interactive'); } /******************************************************************************/