mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-10 01:02:08 +01:00
Add spoof-css
scriptlet
Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/2618 Usage: example.com##+js(spoof-css, selector, property-name, property-value, ...) - selector: a valid CSS selector which matches the elements for which the spoofing must apply. - property-name: a CSS property name (can be dashed- or camel-cased) - property-value: the value to return regardless of the currently computed value. There can be any number of property-name/property-value pairs, all separated by commas. A special property-name/property-value pair `debug/1` can be used to force the browser to break when `getComputedStyle()` or `getBoundingClientrect()` is called, useful to help pinpoint usage of those calls in the page's source code: example.com##+js(spoof-css, .ad, debug, 1)
This commit is contained in:
parent
f0fba0d640
commit
d405460584
1 changed files with 79 additions and 3 deletions
|
@ -52,9 +52,10 @@ function safeSelf() {
|
|||
'addEventListener': self.EventTarget.prototype.addEventListener,
|
||||
'removeEventListener': self.EventTarget.prototype.removeEventListener,
|
||||
'log': console.log.bind(console),
|
||||
'uboLog': function(msg) {
|
||||
if ( msg === '' ) { return; }
|
||||
this.log(`[uBO] ${msg}`);
|
||||
'uboLog': function(...args) {
|
||||
if ( args.length === 0 ) { return; }
|
||||
if ( `${args[0]}` === '' ) { return; }
|
||||
this.log('[uBO]', ...args);
|
||||
},
|
||||
};
|
||||
scriptletGlobals.set('safeSelf', safe);
|
||||
|
@ -1589,6 +1590,12 @@ function alertBuster() {
|
|||
apply: function(a) {
|
||||
console.info(a);
|
||||
},
|
||||
get(target, prop, receiver) {
|
||||
if ( prop === 'toString' ) {
|
||||
return target.toString.bind(target);
|
||||
}
|
||||
return Reflect.get(target, prop, receiver);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2109,4 +2116,73 @@ function callNothrow(
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
builtinScriptlets.push({
|
||||
name: 'spoof-css.js',
|
||||
fn: spoofCSS,
|
||||
});
|
||||
function spoofCSS(
|
||||
selector,
|
||||
...args
|
||||
) {
|
||||
if ( typeof selector !== 'string' ) { return; }
|
||||
if ( selector === '' ) { return; }
|
||||
const toCamelCase = s => s.replace(/-[a-z]/g, s => s.charAt(1).toUpperCase());
|
||||
const propToValueMap = new Map();
|
||||
for ( let i = 0; i < args.length; i += 2 ) {
|
||||
if ( typeof args[i+0] !== 'string' ) { break; }
|
||||
if ( args[i+0] === '' ) { break; }
|
||||
if ( typeof args[i+1] !== 'string' ) { break; }
|
||||
propToValueMap.set(toCamelCase(args[i+0]), args[i+1]);
|
||||
}
|
||||
self.getComputedStyle = new Proxy(self.getComputedStyle, {
|
||||
apply: function(target, thisArg, args) {
|
||||
if ( propToValueMap.has('debug') ) { debugger; } // jshint ignore: line
|
||||
const style = Reflect.apply(target, thisArg, args);
|
||||
const targetElements = new WeakSet(document.querySelectorAll(selector));
|
||||
if ( targetElements.has(args[0]) === false ) { return style; }
|
||||
const proxiedStyle = new Proxy(style, {
|
||||
get(target, prop, receiver) {
|
||||
const normalProp = toCamelCase(prop);
|
||||
const value = propToValueMap.has(normalProp)
|
||||
? propToValueMap.get(normalProp)
|
||||
: Reflect.get(target, prop, receiver);
|
||||
return value;
|
||||
},
|
||||
});
|
||||
return proxiedStyle;
|
||||
},
|
||||
get(target, prop, receiver) {
|
||||
if ( prop === 'toString' ) {
|
||||
return target.toString.bind(target);
|
||||
}
|
||||
return Reflect.get(target, prop, receiver);
|
||||
},
|
||||
});
|
||||
Element.prototype.getBoundingClientRect = new Proxy(Element.prototype.getBoundingClientRect, {
|
||||
apply: function(target, thisArg, args) {
|
||||
if ( propToValueMap.has('debug') ) { debugger; } // jshint ignore: line
|
||||
const rect = Reflect.apply(target, thisArg, args);
|
||||
const targetElements = new WeakSet(document.querySelectorAll(selector));
|
||||
if ( targetElements.has(thisArg) === false ) { return rect; }
|
||||
let { height, width } = rect;
|
||||
if ( propToValueMap.has('width') ) {
|
||||
width = parseFloat(propToValueMap.get('width'));
|
||||
}
|
||||
if ( propToValueMap.has('height') ) {
|
||||
height = parseFloat(propToValueMap.get('height'));
|
||||
}
|
||||
return new self.DOMRect(rect.x, rect.y, width, height);
|
||||
},
|
||||
get(target, prop, receiver) {
|
||||
if ( prop === 'toString' ) {
|
||||
return target.toString.bind(target);
|
||||
}
|
||||
return Reflect.get(target, prop, receiver);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
Loading…
Reference in a new issue