Add experimental href-from-text scriptlet

Related issue:
- https://github.com/uBlockOrigin/uBlock-issues/issues/2531

Usage:

    example.com##+js(href-from-text, a[href^="/tracker-link?to="]

The above scriptlet will find all elements matching the selector
passed as 1st argument, and replace the `href` attribute with the
text content of the element, if all the following conditions are
met:

- The element is a link (`a`) element
- The link element has an existing `href` attribute
- The text content of the element is a valid `https`-based URL
This commit is contained in:
Raymond Hill 2023-03-09 08:49:26 -05:00
parent 7bf3f1bd20
commit e123256eaf
No known key found for this signature in database
GPG key ID: 25E1490B761470C2

View file

@ -1842,6 +1842,81 @@
/// href-from-text.js
(function() {
let selector = '{{1}}';
if ( selector === '{{1}}' ) { selector = ''; }
if ( selector === '' ) { return; }
const sanitizeCopycats = (href, text) => {
let elems = [];
try {
elems = document.querySelectorAll(`a[href="${href}"`);
}
catch(ex) {
}
for ( const elem of elems ) {
elem.setAttribute('href', text);
}
};
const sanitize = ( ) => {
let elems = [];
try {
elems = document.querySelectorAll(selector);
}
catch(ex) {
return false;
}
for ( const elem of elems ) {
if ( elem.localName !== 'a' ) { continue; }
if ( elem.hasAttribute('href') === false ) { continue; }
const href = elem.getAttribute('href');
const text = elem.textContent
.replace(/^[^\x21-\x7e]+/, '') // remove leading invalid characters
.replace(/[^\x21-\x7e]+$/, '') // remove trailing invalid characters
;
if ( /^https:\/\/./.test(text) === false ) { continue; }
if ( /[^\x21-\x7e]/.test(text) ) { continue; }
if ( href === text ) { continue; }
elem.setAttribute('href', text);
sanitizeCopycats(href, text);
}
return true;
};
let observer, timer;
const onDomChanged = mutations => {
if ( timer !== undefined ) { return; }
let shouldSanitize = false;
for ( const mutation of mutations ) {
if ( mutation.addedNodes.length === 0 ) { continue; }
for ( const node of mutation.addedNodes ) {
if ( node.nodeType !== 1 ) { continue; }
shouldSanitize = true;
break;
}
if ( shouldSanitize ) { break; }
}
if ( shouldSanitize === false ) { return; }
timer = self.requestAnimationFrame(( ) => {
timer = undefined;
sanitize();
});
};
const start = ( ) => {
if ( sanitize() === false ) { return; }
observer = new MutationObserver(onDomChanged);
observer.observe(document.body, {
subtree: true,
childList: true,
});
};
if ( document.readyState === 'loading' ) {
document.addEventListener('DOMContentLoaded', start, { once: true });
} else {
start();
}
})();
// These lines below are skipped by the resource parser.
// <<<< end of private namespace
})();