mirror of https://github.com/gorhill/uBlock.git
Add `remove-node-text.js` scriptlet
The scriptlet remove the *whole* text of a DOM node. Usage: example.com##+js(remote-node-text, nodeName, condition, ...) Where `condition` is a pattern to find in the target node for the removal to occur. Since the text of the node is wholly removed, this is not a scriplet which requires a trusted-source, as it is virtually a similar capability as removing DOM nodes through procedural cosmetic operator `:remove()` (which can't target `#text` nodes), which does not require trusted-source. Alias: `rmnt` The extra parameters are the same as those documented for `replace-node-text`. For consistency, the alias for `replace-node-text` has been renamed `rpnt`.
This commit is contained in:
parent
1ff31e0ccd
commit
2bb446797a
|
@ -527,6 +527,93 @@ function setConstantCore(
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
builtinScriptlets.push({
|
||||||
|
name: 'replace-node-text-core.fn',
|
||||||
|
fn: replaceNodeTextCore,
|
||||||
|
dependencies: [
|
||||||
|
'get-extra-args.fn',
|
||||||
|
'pattern-to-regex.fn',
|
||||||
|
'run-at.fn',
|
||||||
|
'safe-self.fn',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
function replaceNodeTextCore(
|
||||||
|
nodeName = '',
|
||||||
|
pattern = '',
|
||||||
|
replacement = ''
|
||||||
|
) {
|
||||||
|
const reNodeName = patternToRegex(nodeName, 'i');
|
||||||
|
const rePattern = patternToRegex(pattern, 'gms');
|
||||||
|
const extraArgs = getExtraArgs(Array.from(arguments), 3);
|
||||||
|
const shouldLog = scriptletGlobals.has('canDebug') && extraArgs.log || 0;
|
||||||
|
const reCondition = patternToRegex(extraArgs.condition || '', 'gms');
|
||||||
|
const safe = safeSelf();
|
||||||
|
const stop = (takeRecord = true) => {
|
||||||
|
if ( takeRecord ) {
|
||||||
|
handleMutations(observer.takeRecords());
|
||||||
|
}
|
||||||
|
observer.disconnect();
|
||||||
|
if ( shouldLog !== 0 ) {
|
||||||
|
safe.uboLog(`sed.js: quitting "${pattern}" => "${replacement}"`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let sedCount = extraArgs.sedCount || 0;
|
||||||
|
const handleNode = node => {
|
||||||
|
const before = node.textContent;
|
||||||
|
if ( safe.RegExp_test.call(rePattern, before) === false ) { return true; }
|
||||||
|
if ( safe.RegExp_test.call(reCondition, before) === false ) { return true; }
|
||||||
|
const after = pattern !== ''
|
||||||
|
? before.replace(rePattern, replacement)
|
||||||
|
: replacement;
|
||||||
|
node.textContent = after;
|
||||||
|
if ( shouldLog !== 0 ) {
|
||||||
|
safe.uboLog('sed.js before:\n', before);
|
||||||
|
safe.uboLog('sed.js after:\n', after);
|
||||||
|
}
|
||||||
|
return sedCount === 0 || (sedCount -= 1) !== 0;
|
||||||
|
};
|
||||||
|
const handleMutations = mutations => {
|
||||||
|
for ( const mutation of mutations ) {
|
||||||
|
for ( const node of mutation.addedNodes ) {
|
||||||
|
if ( reNodeName.test(node.nodeName) === false ) { continue; }
|
||||||
|
if ( handleNode(node) ) { continue; }
|
||||||
|
stop(false); return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const observer = new MutationObserver(handleMutations);
|
||||||
|
observer.observe(document, { childList: true, subtree: true });
|
||||||
|
if ( document.documentElement ) {
|
||||||
|
const treeWalker = document.createTreeWalker(
|
||||||
|
document.documentElement,
|
||||||
|
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT
|
||||||
|
);
|
||||||
|
let count = 0;
|
||||||
|
for (;;) {
|
||||||
|
const node = treeWalker.nextNode();
|
||||||
|
count += 1;
|
||||||
|
if ( node === null ) { break; }
|
||||||
|
if ( reNodeName.test(node.nodeName) === false ) { continue; }
|
||||||
|
if ( handleNode(node) ) { continue; }
|
||||||
|
stop(); break;
|
||||||
|
}
|
||||||
|
if ( shouldLog !== 0 ) {
|
||||||
|
safe.uboLog(`sed.js ${count} nodes present before installing mutation observer`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( extraArgs.stay ) { return; }
|
||||||
|
runAt(( ) => {
|
||||||
|
const quitAfter = extraArgs.quitAfter || 0;
|
||||||
|
if ( quitAfter !== 0 ) {
|
||||||
|
setTimeout(( ) => { stop(); }, quitAfter);
|
||||||
|
} else {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}, 'interactive');
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
|
||||||
Injectable scriptlets
|
Injectable scriptlets
|
||||||
|
@ -2304,6 +2391,25 @@ function spoofCSS(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
builtinScriptlets.push({
|
||||||
|
name: 'remove-node-text.js',
|
||||||
|
aliases: [ 'rmnt.js' ],
|
||||||
|
fn: removeNodeText,
|
||||||
|
world: 'ISOLATED',
|
||||||
|
dependencies: [
|
||||||
|
'replace-node-text-core.fn',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
function removeNodeText(
|
||||||
|
nodeName,
|
||||||
|
condition,
|
||||||
|
...extraArgs
|
||||||
|
) {
|
||||||
|
replaceNodeTextCore(nodeName, '', '', 'condition', condition || '', ...extraArgs);
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
*
|
*
|
||||||
* Scriplets below this section are only available for filter lists from
|
* Scriplets below this section are only available for filter lists from
|
||||||
|
@ -2343,87 +2449,20 @@ function spoofCSS(
|
||||||
builtinScriptlets.push({
|
builtinScriptlets.push({
|
||||||
name: 'replace-node-text.js',
|
name: 'replace-node-text.js',
|
||||||
requiresTrust: true,
|
requiresTrust: true,
|
||||||
aliases: [ 'rnt.js', 'sed.js' /* to be removed */ ],
|
aliases: [ 'rpnt.js', 'sed.js' /* to be removed */ ],
|
||||||
fn: replaceNodeText,
|
fn: replaceNodeText,
|
||||||
world: 'ISOLATED',
|
world: 'ISOLATED',
|
||||||
dependencies: [
|
dependencies: [
|
||||||
'get-extra-args.fn',
|
'replace-node-text-core.fn',
|
||||||
'pattern-to-regex.fn',
|
|
||||||
'run-at.fn',
|
|
||||||
'safe-self.fn',
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
function replaceNodeText(
|
function replaceNodeText(
|
||||||
nodeName = '',
|
nodeName,
|
||||||
pattern = '',
|
pattern,
|
||||||
replacement = ''
|
replacement,
|
||||||
|
...extraArgs
|
||||||
) {
|
) {
|
||||||
const reNodeName = patternToRegex(nodeName, 'i');
|
replaceNodeTextCore(nodeName, pattern, replacement, ...extraArgs);
|
||||||
const rePattern = patternToRegex(pattern, 'gms');
|
|
||||||
const extraArgs = getExtraArgs(Array.from(arguments), 3);
|
|
||||||
const shouldLog = scriptletGlobals.has('canDebug') && extraArgs.log || 0;
|
|
||||||
const reCondition = patternToRegex(extraArgs.condition || '', 'gms');
|
|
||||||
const safe = safeSelf();
|
|
||||||
const stop = (takeRecord = true) => {
|
|
||||||
if ( takeRecord ) {
|
|
||||||
handleMutations(observer.takeRecords());
|
|
||||||
}
|
|
||||||
observer.disconnect();
|
|
||||||
if ( shouldLog !== 0 ) {
|
|
||||||
safe.uboLog(`sed.js: quitting "${pattern}" => "${replacement}"`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let sedCount = extraArgs.sedCount || 0;
|
|
||||||
const handleNode = node => {
|
|
||||||
const before = node.textContent;
|
|
||||||
if ( safe.RegExp_test.call(rePattern, before) === false ) { return true; }
|
|
||||||
if ( safe.RegExp_test.call(reCondition, before) === false ) { return true; }
|
|
||||||
const after = before.replace(rePattern, replacement);
|
|
||||||
node.textContent = after;
|
|
||||||
if ( shouldLog !== 0 ) {
|
|
||||||
safe.uboLog('sed.js before:\n', before);
|
|
||||||
safe.uboLog('sed.js after:\n', after);
|
|
||||||
}
|
|
||||||
return sedCount === 0 || (sedCount -= 1) !== 0;
|
|
||||||
};
|
|
||||||
const handleMutations = mutations => {
|
|
||||||
for ( const mutation of mutations ) {
|
|
||||||
for ( const node of mutation.addedNodes ) {
|
|
||||||
if ( reNodeName.test(node.nodeName) === false ) { continue; }
|
|
||||||
if ( handleNode(node) ) { continue; }
|
|
||||||
stop(false); return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const observer = new MutationObserver(handleMutations);
|
|
||||||
observer.observe(document, { childList: true, subtree: true });
|
|
||||||
if ( document.documentElement ) {
|
|
||||||
const treeWalker = document.createTreeWalker(
|
|
||||||
document.documentElement,
|
|
||||||
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT
|
|
||||||
);
|
|
||||||
let count = 0;
|
|
||||||
for (;;) {
|
|
||||||
const node = treeWalker.nextNode();
|
|
||||||
count += 1;
|
|
||||||
if ( node === null ) { break; }
|
|
||||||
if ( reNodeName.test(node.nodeName) === false ) { continue; }
|
|
||||||
if ( handleNode(node) ) { continue; }
|
|
||||||
stop(); break;
|
|
||||||
}
|
|
||||||
if ( shouldLog !== 0 ) {
|
|
||||||
safe.uboLog(`sed.js ${count} nodes present before installing mutation observer`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( extraArgs.stay ) { return; }
|
|
||||||
runAt(( ) => {
|
|
||||||
const quitAfter = extraArgs.quitAfter || 0;
|
|
||||||
if ( quitAfter !== 0 ) {
|
|
||||||
setTimeout(( ) => { stop(); }, quitAfter);
|
|
||||||
} else {
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
}, 'interactive');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
|
Loading…
Reference in New Issue