forked from mirrors/cursor-free-vip
Big Change Update
This commit is contained in:
188
uBlock0.chromium/js/resources/href-sanitizer.js
Normal file
188
uBlock0.chromium/js/resources/href-sanitizer.js
Normal file
@@ -0,0 +1,188 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2019-present Raymond Hill
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
|
||||
*/
|
||||
|
||||
import { registerScriptlet } from './base.js';
|
||||
import { runAt } from './run-at.js';
|
||||
import { safeSelf } from './safe-self.js';
|
||||
import { urlSkip } from '../urlskip.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
registerScriptlet(urlSkip, {
|
||||
name: 'urlskip.fn',
|
||||
});
|
||||
|
||||
/**
|
||||
* @scriptlet href-sanitizer
|
||||
*
|
||||
* @description
|
||||
* Set the `href` attribute to a value found in the DOM at, or below the
|
||||
* targeted `a` element, and optionally with transformation steps.
|
||||
*
|
||||
* @param selector
|
||||
* A plain CSS selector for elements which `href` property must be sanitized.
|
||||
*
|
||||
* @param source
|
||||
* One or more tokens to lookup the source of the `href` property, and
|
||||
* optionally the transformation steps to perform:
|
||||
* - `text`: Use the text content of the element as the URL
|
||||
* - `[name]`: Use the value of the attribute `name` as the URL
|
||||
* - Transformation steps: see `urlskip` documentation
|
||||
*
|
||||
* If `text` or `[name]` is not present, the URL will be the value of `href`
|
||||
* attribute.
|
||||
*
|
||||
* @example
|
||||
* `example.org##+js(href-sanitizer, a)`
|
||||
* `example.org##+js(href-sanitizer, a[title], [title])`
|
||||
* `example.org##+js(href-sanitizer, a[href*="/away.php?to="], ?to)`
|
||||
* `example.org##+js(href-sanitizer, a[href*="/redirect"], ?url ?url -base64)`
|
||||
*
|
||||
* */
|
||||
|
||||
function hrefSanitizer(
|
||||
selector = '',
|
||||
source = ''
|
||||
) {
|
||||
if ( typeof selector !== 'string' ) { return; }
|
||||
if ( selector === '' ) { return; }
|
||||
const safe = safeSelf();
|
||||
const logPrefix = safe.makeLogPrefix('href-sanitizer', selector, source);
|
||||
if ( source === '' ) { source = 'text'; }
|
||||
const sanitizeCopycats = (href, text) => {
|
||||
let elems = [];
|
||||
try {
|
||||
elems = document.querySelectorAll(`a[href="${href}"`);
|
||||
}
|
||||
catch(ex) {
|
||||
}
|
||||
for ( const elem of elems ) {
|
||||
elem.setAttribute('href', text);
|
||||
}
|
||||
return elems.length;
|
||||
};
|
||||
const validateURL = text => {
|
||||
if ( typeof text !== 'string' ) { return ''; }
|
||||
if ( text === '' ) { return ''; }
|
||||
if ( /[\x00-\x20\x7f]/.test(text) ) { return ''; }
|
||||
try {
|
||||
const url = new URL(text, document.location);
|
||||
return url.href;
|
||||
} catch(ex) {
|
||||
}
|
||||
return '';
|
||||
};
|
||||
const extractParam = (href, source) => {
|
||||
if ( Boolean(source) === false ) { return href; }
|
||||
const recursive = source.includes('?', 1);
|
||||
const end = recursive ? source.indexOf('?', 1) : source.length;
|
||||
try {
|
||||
const url = new URL(href, document.location);
|
||||
let value = url.searchParams.get(source.slice(1, end));
|
||||
if ( value === null ) { return href }
|
||||
if ( recursive ) { return extractParam(value, source.slice(end)); }
|
||||
return value;
|
||||
} catch(x) {
|
||||
}
|
||||
return href;
|
||||
};
|
||||
const extractURL = (elem, source) => {
|
||||
if ( /^\[.*\]$/.test(source) ) {
|
||||
return elem.getAttribute(source.slice(1,-1).trim()) || '';
|
||||
}
|
||||
if ( source === 'text' ) {
|
||||
return elem.textContent
|
||||
.replace(/^[^\x21-\x7e]+/, '') // remove leading invalid characters
|
||||
.replace(/[^\x21-\x7e]+$/, '') // remove trailing invalid characters
|
||||
;
|
||||
}
|
||||
if ( source.startsWith('?') === false ) { return ''; }
|
||||
const steps = source.replace(/(\S)\?/g, '\\1?').split(/\s+/);
|
||||
const url = steps.length === 1
|
||||
? extractParam(elem.href, source)
|
||||
: urlSkip(elem.href, false, steps);
|
||||
if ( url === undefined ) { return; }
|
||||
return url.replace(/ /g, '%20');
|
||||
};
|
||||
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 = extractURL(elem, source);
|
||||
const hrefAfter = validateURL(text);
|
||||
if ( hrefAfter === '' ) { continue; }
|
||||
if ( hrefAfter === href ) { continue; }
|
||||
elem.setAttribute('href', hrefAfter);
|
||||
const count = sanitizeCopycats(href, hrefAfter);
|
||||
safe.uboLog(logPrefix, `Sanitized ${count+1} links to\n${hrefAfter}`);
|
||||
}
|
||||
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 = safe.onIdle(( ) => {
|
||||
timer = undefined;
|
||||
sanitize();
|
||||
});
|
||||
};
|
||||
const start = ( ) => {
|
||||
if ( sanitize() === false ) { return; }
|
||||
observer = new MutationObserver(onDomChanged);
|
||||
observer.observe(document.body, {
|
||||
subtree: true,
|
||||
childList: true,
|
||||
});
|
||||
};
|
||||
runAt(( ) => { start(); }, 'interactive');
|
||||
}
|
||||
registerScriptlet(hrefSanitizer, {
|
||||
name: 'href-sanitizer.js',
|
||||
world: 'ISOLATED',
|
||||
aliases: [
|
||||
'urlskip.js',
|
||||
],
|
||||
dependencies: [
|
||||
runAt,
|
||||
safeSelf,
|
||||
urlSkip,
|
||||
],
|
||||
});
|
||||
Reference in New Issue
Block a user