Add set-attr scriptlet · gorhill/uBlock@786d9b2 (original) (raw)
`@@ -2557,11 +2557,46 @@ function m3uPrune(
`
2557
2557
`});
`
2558
2558
`}
`
2559
2559
``
2560
``
`-
/******************************************************************************/
`
``
2560
`+
/*******************************************************************************
`
``
2561
`+
*
`
``
2562
`+
- @scriptlet href-sanitizer
`
``
2563
`+
*
`
``
2564
`+
- @description
`
``
2565
`` +
- Set the
hrefattribute to a value found in the DOM at, or below the
``
``
2566
`` +
- targeted
aelement.
``
``
2567
`+
*
`
``
2568
`+
Syntax
`
``
2569
`+
*
`
``
2570
* ```text
``
2571
`+
- example.org##+js(href-sanitizer, selector [, source])
`
``
2572
* ```
``
2573
`+
*
`
``
2574
`` +
selector: required, CSS selector, specifiesaelements for which the
``
``
2575
`` +
hrefattribute must be overriden.
``
``
2576
`` +
source: optional, default totext, specifies from where to get the
``
``
2577
`` +
- value which will override the
hrefattribute.
``
``
2578
`` +
text: the value will be the first valid URL found in the text
``
``
2579
`` +
- content of the targeted
aelement.
``
``
2580
`` +
[attr]: the value will be the attribute attr of the targeteda
``
``
2581
`+
- element.
`
``
2582
`` +
?param: the value will be the query parameter param of the URL
``
``
2583
`` +
- found in the
hrefattribute of the targetedaelement.
``
``
2584
`+
*
`
``
2585
`+
Examples
`
``
2586
`+
*
`
``
2587
`+
- example.org##+js(href-sanitizer, a)
`
``
2588
`+
- example.org##+js(href-sanitizer, a[title], [title])
`
``
2589
`+
- example.org##+js(href-sanitizer, a[href*="/away.php?to="], ?to)
`
``
2590
`+
*
`
``
2591
`+
- */
`
2561
2592
``
2562
2593
`builtinScriptlets.push({
`
2563
2594
`name: 'href-sanitizer.js',
`
2564
2595
`fn: hrefSanitizer,
`
``
2596
`+
world: 'ISOLATED',
`
``
2597
`+
dependencies: [
`
``
2598
`+
'run-at.fn',
`
``
2599
`+
],
`
2565
2600
`});
`
2566
2601
`function hrefSanitizer(
`
2567
2602
`selector = '',
`
`@@ -2659,14 +2694,32 @@ function hrefSanitizer(
`
2659
2694
`childList: true,
`
2660
2695
`});
`
2661
2696
`};
`
2662
``
`-
if ( document.readyState === 'loading' ) {
`
2663
``
`-
document.addEventListener('DOMContentLoaded', start, { once: true });
`
2664
``
`-
} else {
`
2665
``
`-
start();
`
2666
``
`-
}
`
``
2697
`+
runAt(( ) => { start(); }, 'interactive');
`
2667
2698
`}
`
2668
2699
``
2669
``
`-
/******************************************************************************/
`
``
2700
`+
/*******************************************************************************
`
``
2701
`+
*
`
``
2702
`+
- @scriptlet call-nothrow
`
``
2703
`+
*
`
``
2704
`+
- @description
`
``
2705
`+
- Prevent a function call from throwing. The function will be called, however
`
``
2706
`+
- should it throw, the scriptlet will silently process the exception and
`
``
2707
`+
- returns as if no exception has occurred.
`
``
2708
`+
*
`
``
2709
`+
Syntax
`
``
2710
`+
*
`
``
2711
* ```text
``
2712
`+
- example.org##+js(call-nothrow, propertyChain)
`
``
2713
* ```
``
2714
`+
*
`
``
2715
`` +
propertyChain: a chain of dot-separated properties which leads to the
``
``
2716
`+
- function to be trapped.
`
``
2717
`+
*
`
``
2718
`+
Examples
`
``
2719
`+
*
`
``
2720
`+
- example.org##+js(call-nothrow, Object.defineProperty)
`
``
2721
`+
*
`
``
2722
`+
- */
`
2670
2723
``
2671
2724
`builtinScriptlets.push({
`
2672
2725
`name: 'call-nothrow.js',
`
`@@ -2899,6 +2952,118 @@ function setSessionStorageItem(key = '', value = '') {
`
2899
2952
`setLocalStorageItemCore('session', false, key, value);
`
2900
2953
`}
`
2901
2954
``
``
2955
`+
/*******************************************************************************
`
``
2956
`+
*
`
``
2957
`+
- @scriptlet set-attr
`
``
2958
`+
*
`
``
2959
`+
- @description
`
``
2960
`+
- Sets the specified attribute on the specified elements. This scriptlet runs
`
``
2961
`+
- once when the page loads then afterward on DOM mutations.
`
``
2962
+
``
2963
`+
`
``
2964
`+
*
`
``
2965
`+
Syntax
`
``
2966
`+
*
`
``
2967
* ```text
``
2968
`+
- example.org##+js(set-attr, selector, attr [, value])
`
``
2969
* ```
``
2970
`+
*
`
``
2971
`` +
selector: CSS selector of DOM elements for which the attributeattr
``
``
2972
`+
- must be modified.
`
``
2973
`` +
attr: the name of the attribute to modify
``
``
2974
`` +
value: the value to assign to the target attribute. Possible values:
``
``
2975
`` +
'': empty string (default)
``
``
2976
`` +
true
``
``
2977
`` +
false
``
``
2978
`+
- positive decimal integer 0 <= value < 32768
`
``
2979
`` +
[other]: copy the value from attributeotheron the same element
``
``
2980
`+
- */
`
``
2981
+
``
2982
`+
builtinScriptlets.push({
`
``
2983
`+
name: 'set-attr.js',
`
``
2984
`+
fn: setAttr,
`
``
2985
`+
world: 'ISOLATED',
`
``
2986
`+
dependencies: [
`
``
2987
`+
'run-at.fn',
`
``
2988
`+
],
`
``
2989
`+
});
`
``
2990
`+
function setAttr(
`
``
2991
`+
selector = '',
`
``
2992
`+
attr = '',
`
``
2993
`+
value = ''
`
``
2994
`+
) {
`
``
2995
`+
if ( typeof selector !== 'string' ) { return; }
`
``
2996
`+
if ( selector === '' ) { return; }
`
``
2997
`+
if ( value === '' ) { return; }
`
``
2998
+
``
2999
`+
const validValues = [ '', 'false', 'true' ];
`
``
3000
`+
let copyFrom = '';
`
``
3001
+
``
3002
`+
if ( validValues.includes(value) === false ) {
`
``
3003
`+
if ( /^\d+$/.test(value) ) {
`
``
3004
`+
const n = parseInt(value, 10);
`
``
3005
`+
if ( n >= 32768 ) { return; }
`
``
3006
`` +
value = ${n};
``
``
3007
`+
} else if ( /^[.+]$/.test(value) ) {
`
``
3008
`+
copyFrom = value.slice(1, -1);
`
``
3009
`+
} else {
`
``
3010
`+
return;
`
``
3011
`+
}
`
``
3012
`+
}
`
``
3013
+
``
3014
`+
const extractValue = elem => {
`
``
3015
`+
if ( copyFrom !== '' ) {
`
``
3016
`+
return elem.getAttribute(copyFrom) || '';
`
``
3017
`+
}
`
``
3018
`+
return value;
`
``
3019
`+
};
`
``
3020
+
``
3021
`+
const applySetAttr = ( ) => {
`
``
3022
`+
const elems = [];
`
``
3023
`+
try {
`
``
3024
`+
elems.push(...document.querySelectorAll(selector));
`
``
3025
`+
}
`
``
3026
`+
catch(ex) {
`
``
3027
`+
return false;
`
``
3028
`+
}
`
``
3029
`+
for ( const elem of elems ) {
`
``
3030
`+
const before = elem.getAttribute(attr);
`
``
3031
`+
const after = extractValue(elem);
`
``
3032
`+
if ( after === before ) { continue; }
`
``
3033
`+
elem.setAttribute(attr, after);
`
``
3034
`+
}
`
``
3035
`+
return true;
`
``
3036
`+
};
`
``
3037
`+
let observer, timer;
`
``
3038
`+
const onDomChanged = mutations => {
`
``
3039
`+
if ( timer !== undefined ) { return; }
`
``
3040
`+
let shouldWork = false;
`
``
3041
`+
for ( const mutation of mutations ) {
`
``
3042
`+
if ( mutation.addedNodes.length === 0 ) { continue; }
`
``
3043
`+
for ( const node of mutation.addedNodes ) {
`
``
3044
`+
if ( node.nodeType !== 1 ) { continue; }
`
``
3045
`+
shouldWork = true;
`
``
3046
`+
break;
`
``
3047
`+
}
`
``
3048
`+
if ( shouldWork ) { break; }
`
``
3049
`+
}
`
``
3050
`+
if ( shouldWork === false ) { return; }
`
``
3051
`+
timer = self.requestAnimationFrame(( ) => {
`
``
3052
`+
timer = undefined;
`
``
3053
`+
applySetAttr();
`
``
3054
`+
});
`
``
3055
`+
};
`
``
3056
`+
const start = ( ) => {
`
``
3057
`+
if ( applySetAttr() === false ) { return; }
`
``
3058
`+
observer = new MutationObserver(onDomChanged);
`
``
3059
`+
observer.observe(document.body, {
`
``
3060
`+
subtree: true,
`
``
3061
`+
childList: true,
`
``
3062
`+
});
`
``
3063
`+
};
`
``
3064
`+
runAt(( ) => { start(); }, 'idle');
`
``
3065
`+
}
`
``
3066
+
2902
3067
``
2903
3068
`/*******************************************************************************
`
2904
3069
` *
`