Skip to content

Commit e123256

Browse files
committed
Add experimental href-from-text scriptlet
Related issue: - uBlockOrigin/uBlock-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
1 parent 7bf3f1b commit e123256

File tree

1 file changed

+75
-0
lines changed

1 file changed

+75
-0
lines changed

assets/resources/scriptlets.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1842,6 +1842,81 @@
18421842

18431843

18441844

1845+
/// href-from-text.js
1846+
(function() {
1847+
let selector = '{{1}}';
1848+
if ( selector === '{{1}}' ) { selector = ''; }
1849+
if ( selector === '' ) { return; }
1850+
const sanitizeCopycats = (href, text) => {
1851+
let elems = [];
1852+
try {
1853+
elems = document.querySelectorAll(`a[href="${href}"`);
1854+
}
1855+
catch(ex) {
1856+
}
1857+
for ( const elem of elems ) {
1858+
elem.setAttribute('href', text);
1859+
}
1860+
};
1861+
const sanitize = ( ) => {
1862+
let elems = [];
1863+
try {
1864+
elems = document.querySelectorAll(selector);
1865+
}
1866+
catch(ex) {
1867+
return false;
1868+
}
1869+
for ( const elem of elems ) {
1870+
if ( elem.localName !== 'a' ) { continue; }
1871+
if ( elem.hasAttribute('href') === false ) { continue; }
1872+
const href = elem.getAttribute('href');
1873+
const text = elem.textContent
1874+
.replace(/^[^\x21-\x7e]+/, '') // remove leading invalid characters
1875+
.replace(/[^\x21-\x7e]+$/, '') // remove trailing invalid characters
1876+
;
1877+
if ( /^https:\/\/./.test(text) === false ) { continue; }
1878+
if ( /[^\x21-\x7e]/.test(text) ) { continue; }
1879+
if ( href === text ) { continue; }
1880+
elem.setAttribute('href', text);
1881+
sanitizeCopycats(href, text);
1882+
}
1883+
return true;
1884+
};
1885+
let observer, timer;
1886+
const onDomChanged = mutations => {
1887+
if ( timer !== undefined ) { return; }
1888+
let shouldSanitize = false;
1889+
for ( const mutation of mutations ) {
1890+
if ( mutation.addedNodes.length === 0 ) { continue; }
1891+
for ( const node of mutation.addedNodes ) {
1892+
if ( node.nodeType !== 1 ) { continue; }
1893+
shouldSanitize = true;
1894+
break;
1895+
}
1896+
if ( shouldSanitize ) { break; }
1897+
}
1898+
if ( shouldSanitize === false ) { return; }
1899+
timer = self.requestAnimationFrame(( ) => {
1900+
timer = undefined;
1901+
sanitize();
1902+
});
1903+
};
1904+
const start = ( ) => {
1905+
if ( sanitize() === false ) { return; }
1906+
observer = new MutationObserver(onDomChanged);
1907+
observer.observe(document.body, {
1908+
subtree: true,
1909+
childList: true,
1910+
});
1911+
};
1912+
if ( document.readyState === 'loading' ) {
1913+
document.addEventListener('DOMContentLoaded', start, { once: true });
1914+
} else {
1915+
start();
1916+
}
1917+
})();
1918+
1919+
18451920
// These lines below are skipped by the resource parser.
18461921
// <<<< end of private namespace
18471922
})();

0 commit comments

Comments
 (0)